diff options
author | Martin Odersky <odersky@gmail.com> | 2009-12-07 17:31:49 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2009-12-07 17:31:49 +0000 |
commit | 1e88594f358d08b5e9b22ba87280003a581359e4 (patch) | |
tree | e68b6d1095ff1b1345e86d8a67c1f874849a31ff /src | |
parent | de1d172a15e20ec7e96d96d5cea5cb41162b9f75 (diff) | |
download | scala-1e88594f358d08b5e9b22ba87280003a581359e4.tar.gz scala-1e88594f358d08b5e9b22ba87280003a581359e4.tar.bz2 scala-1e88594f358d08b5e9b22ba87280003a581359e4.zip |
new doc comment generation, including some new ...
new doc comment generation, including some new style doc comments in
collection classes.
Diffstat (limited to 'src')
24 files changed, 1074 insertions, 446 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 6b2f3101a6..645eb1ecf6 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -115,31 +115,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val treeBrowser = treeBrowsers.create() - -// val copy = new LazyTreeCopier() - - /** A map of all doc comments, indexed by symbols. - * Only active in onlyPresentation mode - */ - val comments = - if (onlyPresentation) new HashMap[Symbol,String] - else null - - /** A map of all doc comments source file offsets, - * indexed by symbols. - * Only active in onlyPresentation mode - */ - val commentOffsets = - if (onlyPresentation) new HashMap[Symbol,Int] - else null - - /** A map of argument names for methods - * !!! can be dropped once named method arguments are in !!! - */ - val methodArgumentNames = - if (onlyPresentation) new HashMap[Symbol,List[List[Symbol]]] - else null - // ------------ Hooks for interactive mode------------------------- /** Called every time an AST node is succesfully typedchecked in typerPhase. @@ -302,8 +277,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable val unit0 = currentRun.currentUnit try { currentRun.currentUnit = unit - reporter.setSource(unit.source) - if (!cancelled(unit)) apply(unit) + if (!cancelled(unit)) + reporter.withSource(unit.source) { apply(unit) } currentRun.advanceUnit } finally { //assert(currentRun.currentUnit == unit) @@ -889,11 +864,11 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable var localPhase = firstPhase.asInstanceOf[GlobalPhase] while (localPhase != null && (localPhase.id < globalPhase.id || localPhase.id <= namerPhase.id)/* && !reporter.hasErrors*/) { val oldSource = reporter.getSource - reporter.setSource(unit.source) - atPhase(localPhase)(localPhase.applyPhase(unit)) + reporter.withSource(unit.source) { + atPhase(localPhase)(localPhase.applyPhase(unit)) + } val newLocalPhase = localPhase.next.asInstanceOf[GlobalPhase] localPhase = if (localPhase == newLocalPhase) null else newLocalPhase - reporter.setSource(oldSource) } refreshProgress } diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala new file mode 100755 index 0000000000..d754f12dee --- /dev/null +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala @@ -0,0 +1,365 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2009 LAMP/EPFL + * @author Martin Odersky + */ +// $Id: Unapplies.scala 19206 2009-10-21 20:57:27Z extempore $ + +package scala.tools.nsc +package ast + +import symtab._ +import util.{Position, NoPosition} +import scala.collection.mutable.{HashMap, ListBuffer, StringBuilder} + +/* + * @author Martin Odersky + * @version 1.0 + */ +trait DocComments { self: SymbolTable => + + val docComments = new HashMap[Symbol, DocComment] // !!! todo: inherit from comment? + + private val defs = new HashMap[Symbol, Map[String, String]] { + override def default(key: Symbol) = Map() + } + + def getDocComment(sym: Symbol): Option[DocComment] = { + docComments get sym match { + case None => + mapFind(sym.allOverriddenSymbols)(docComments.get) + case someSym => + someSym + } + } + + private def mapFind[A, B](xs: Iterable[A])(f: A => Option[B]): Option[B] = { + var res: Option[B] = None + val it = xs.iterator + while (res.isEmpty && it.hasNext) { + res = f(it.next()) + } + res + } + + /** The raw doc comment of symbol `sym`, as it appears in the source text, "" if missing. + */ + def rawDocComment(sym: Symbol): String = getDocComment(sym) map (_.raw) getOrElse "" + + /** The processed doc comment of symbol `sym`, where + * Wiki syntax is expanded and @usecase or @define parts are removed. + * No variables are expanded yet. "" if missing. + */ + def templateDocComment(sym: Symbol): String = getDocComment(sym) map (_.template) getOrElse "" + + /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. + * @param sym The symbol for which doc comment is returned + * @param site The class for which doc comments are generated + */ + def cookedDocComment(sym: Symbol, site: Symbol): String = + getDocComment(sym) map (_.expanded(site)) getOrElse "" + + /** The cooked doc comment of symbol `sym` after variable expansion, or "" if missing. + * @param sym The symbol for which doc comment is returned (site is always the containing class) + */ + def cookedDocComment(sym: Symbol): String = cookedDocComment(sym, sym.owner) + + /** The position of the doc comment of symbol `sym`, or NoPosition if missing */ + def docCommentPos(sym: Symbol): Position = getDocComment(sym) map (_.pos) getOrElse NoPosition + + def useCases(sym: Symbol, site: Symbol): List[(Symbol, String)] = { + def getUseCases(dc: DocComment) = { + for (uc <- dc.useCases; defn <- uc.expandedDefs(site)) yield + (defn, uc.comment.expanded(site)) + } + getDocComment(sym) map getUseCases getOrElse List() + } + + def useCases(sym: Symbol): List[(Symbol, String)] = useCases(sym, sym.owner) + + private val usecasePrefix = "@usecase " + private val definePrefix = "@define " + + /** Returns index of string `str` after `in` skipping longest + * sequence of space and tab characters. + */ + private def skipWhitespace(str: String, in: Int): Int = { + var idx = in + do { + idx += 1 + } while (idx < str.length && (str charAt idx) == ' ' || (str charAt idx) == '\t') + idx + } + + /** Returns index of string `str` after `in` skipping longest + * sequence of space and tab characters, possibly also containing + * a single `*' character. + */ + private def skipLineLead(str: String, in: Int, end: Int): Int = { + val idx = skipWhitespace(str, in) + if (idx < end && (str charAt idx) == '*') skipWhitespace(str, idx) + else idx + } + + /** Extracts variable name from a string, stripping any pair of surrounding braces */ + private 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 + + private def isVarPart(ch: Char) = + '0' <= ch && ch <= '9' || 'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z' + + /** Returns index following variable, or start index if no variable was recognized + */ + private 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 + } + } + + private val wikiReplacements = List( + ("""(\n\s*\*?)(\s*\n)""" .r, """$1 <p>$2"""), + ("""\{\{\{(.*(?:\n.*)*)\}\}\}""".r, """<pre>$1</pre>"""), + ("""`([^`]*)`""" .r, """<code>$1</code>"""), + ("""__([^_]*)__""" .r, """<u>$1</u>"""), + ("""''([^']*)''""" .r, """<i>$1</i>"""), + ("""'''([^']*)'''""" .r, """<b>$1</b>"""), + ("""\^([^^]*)\^""" .r, """<sup>$1</sup>"""), + (""",,([^,]*),,""" .r, """<sub>$1</sub>""")) + private def expandWiki(str: String): String = + (str /: wikiReplacements) { (str1, regexRepl) => regexRepl._1 replaceAllIn(str1, regexRepl._2) } + + /** Lookup definition of variable. + * + * @param vble The variable for which a definition is searched + * @param owner The current owner in which variable definitions are searched. + * @param site The class for which doc comments are generated + */ + def lookupVariable(vble: String, site: Symbol): Option[String] = + if (site == NoSymbol) + None + else + mapFind(site.info.baseClasses)(defs(_).get(vble)) match { + case None => lookupVariable(vble, site.owner) + case someStr => someStr + } + + private var expandCount = 0 + private final val expandLimit = 10 + + /** Expand variable occurrences in string `str', until a fix point is reached or + * a expandLimit is exceeded. + * + * @param str The string to be expanded + * @param site The class for which doc comments are generated + * @return Expanded string + */ + private def expandVariables(str: String, site: Symbol): String = + if (expandCount < expandLimit) { + try { + val out = new StringBuilder + var start = 0 + var idx = 0 + while (idx < str.length) { + if ((str charAt idx) == '$') { + val vstart = idx + idx = skipVariable(str, idx + 1) + val vname = variableName(str.substring(vstart + 1, idx)) + if (vname.length > 0) { + lookupVariable(vname, site) match { + case Some(replacement) => + out append str.substring(start, vstart) + out append replacement + start = idx + case None => + println("no replacement for "+vname) // !!! + } + } else { + idx += 1 + } + } else { + idx += 1 + } + } + if (out.length == 0) str + else { + out append str.substring(start) + expandVariables(out.toString, site) + } + } finally { + expandCount -= 1 + } + } else throw new ExpansionLimitExceeded(str) + + case class DocComment(raw: String, pos: Position = NoPosition) { + + lazy val (template, defines, useCases) = { + val parts = decompose(raw) + val (defines, usecases) = parts.tail partition (_._1 startsWith definePrefix) + val templ = expandWiki(parts.head._1) + (templ, + defines map (d => expandWiki(d._1)), + usecases map (decomposeUseCase(_, templ))) + } + + def expanded(site: Symbol): String = + expandVariables(template, site) +/* + expansions get site match { + case Some(str) => str + case None => val str = + expandVariables(template, sym, site) + expansions += (site -> str) + str + } + private var expansions: Map[Symbol, String] = Map() +*/ + + + /** Decomposes a comment string into + * an initial comment and a list of @define and @usecase clauses, each with starting index + */ + private def decompose(str: String): List[(String, Int)] = { + val out = new ListBuffer[(String, Int)] + var segstart = 0 + var idx = 3 // skip initial "/**" + val end = str.length - 2 // stop before final "*/" + var eolIdx = idx + while (idx < end) { + if ((str charAt idx) == '\n') { + eolIdx = idx + idx = skipLineLead(str, idx, end) + if ((str charAt idx) == '@' && + (str.startsWith(definePrefix, idx) || str.startsWith(usecasePrefix, idx))) { + var segment = str.substring(segstart, eolIdx) + if (segstart == 0) segment += "*/" + out += ((segment, segstart)) + segstart = idx + } + } else idx += 1 + } + if (segstart == 0) + List((str, 0)) + else { + out += ((str.substring(segstart, eolIdx), segstart)) + out.toList + } + } + + def subPos(start: Int, end: Int) = + if (pos == NoPosition) NoPosition + else { + val start1 = pos.start + start + val end1 = pos.end + end + pos withStart start1 withPoint start1 withEnd end1 + } + + def decomposeUseCase(stroff: (String, Int), mainComment: String): UseCase = { + val str = stroff._1 + val offset = stroff._2 + val start = usecasePrefix.length + var idx = start + while (idx < str.length && (str charAt idx) != '\n') idx += 1 + val code = str.substring(start, idx) + val codePos = subPos(offset + usecasePrefix.length, offset + idx) + var firstParBreak = mainComment indexOf "<p>" + if (firstParBreak == -1) firstParBreak = mainComment.length - 2 + val comment = mainComment.substring(0, firstParBreak)+"<p>"+ + str.substring(skipLineLead(str, idx, str.length))+"*/" + val commentPos = subPos(offset + idx, offset + str.length) + UseCase(DocComment(comment, commentPos), code, codePos) + } + + def defineVariables(sym: Symbol) { + for (str <- defines) { + val start = definePrefix.length + var idx = skipVariable(str, start) + val vble = variableName(str.substring(start, idx)) + if (idx < str.length && (str charAt idx) == ' ') idx += 1 + defs(sym) += vble -> str.substring(idx) + } + if (defs(sym).nonEmpty) println("vars of "+sym+" = "+defs(sym)) // !!! + } + } + + case class UseCase(comment: DocComment, body: String, pos: Position) { + var defined: List[Symbol] = List() + var aliases: List[Symbol] = List() + + def expandedDefs(site: Symbol): List[Symbol] = { + + def select(site: Type, name: Name, orElse: => Type): Type = { + val member = site.nonPrivateMember(name) + if (member.isTerm) SingleType(site, member) + else if (member.isType) site.memberType(member) + else orElse + } + + def getSite(name: Name): Type = { + if (name == nme.this_) site.thisType + else { + def findIn(sites: List[Symbol]): Type = sites match { + case List() => NoType + case site :: sites1 => select(site.thisType, name, findIn(sites1)) + } + val (classes, pkgs) = site.ownerChain.span(!_.isPackageClass) + findIn(classes ::: List(pkgs.head, definitions.RootClass)) + } + } + + def getType(str: String): Type = { + val parts = str.split("""\.""").toList + val partnames = (parts.init map newTermName) ::: List(newTypeName(parts.last)) + (getSite(partnames.head) /: partnames.tail)(select(_, _, NoType)) + } + + val aliasExpansions: List[Type] = + for (alias <- aliases) yield + lookupVariable(alias.name.toString.substring(1), site) match { + case Some(repl) => + val tpe = getType(repl) + if (tpe != NoType) tpe + else { + val alias1 = alias.cloneSymbol(definitions.RootClass) + alias1.name = repl.toTypeName + TypeRef(NoPrefix, alias1, List()) + } + case None => + TypeRef(NoPrefix, alias, List()) + } + + def subst(sym: Symbol, from: List[Symbol], to: List[Type]): Type = + if (from.isEmpty) sym.tpe + else if (from.head == sym) to.head + else subst(sym, from.tail, to.tail) + + val substAliases = new TypeMap { + def apply(tp: Type) = mapOver(tp) match { + case tp1 @ TypeRef(pre, sym, args) if (sym.name.length > 1 && sym.name(0) == '$') => + subst(sym, aliases, aliasExpansions) match { + case TypeRef(pre, sym1, _) => + TypeRef(pre, sym1, args) + case _ => + tp1 + } + case tp1 => + tp1 + } + } + + for (defn <- defined) yield + defn.cloneSymbol(site).setInfo( + substAliases(defn.info).asSeenFrom(site.thisType, defn.owner)) + } + } + + class ExpansionLimitExceeded(str: String) extends Exception +} diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala index d18ecbbd37..4ba5f17a40 100644 --- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala +++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala @@ -215,7 +215,7 @@ abstract class TreePrinters { } case DocDef(comment, definition) => - print(comment); println; print(definition) + print(comment.raw); println; print(definition) case Template(parents, self, body) => val currentOwner1 = currentOwner diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 6c6311f143..3af729d4d7 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -570,7 +570,7 @@ trait Trees { // It's used primarily as a marker to check that the import has been typechecked. /** Documented definition, eliminated by analyzer */ - case class DocDef(comment: String, definition: Tree) + case class DocDef(comment: DocComment, definition: Tree) extends Tree { override def symbol: Symbol = definition.symbol override def symbol_=(sym: Symbol) { definition.symbol = sym } @@ -1072,7 +1072,7 @@ trait Trees { def TypeDef(tree: Tree, mods: Modifiers, name: Name, tparams: List[TypeDef], rhs: Tree): TypeDef def LabelDef(tree: Tree, name: Name, params: List[Ident], rhs: Tree): LabelDef def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]): Import - def DocDef(tree: Tree, comment: String, definition: Tree): DocDef + def DocDef(tree: Tree, comment: DocComment, definition: Tree): DocDef def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]): Template def Block(tree: Tree, stats: List[Tree], expr: Tree): Block def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef @@ -1127,7 +1127,7 @@ trait Trees { new LabelDef(name, params, rhs).copyAttrs(tree) def Import(tree: Tree, expr: Tree, selectors: List[ImportSelector]) = new Import(expr, selectors).copyAttrs(tree) - def DocDef(tree: Tree, comment: String, definition: Tree) = + def DocDef(tree: Tree, comment: DocComment, definition: Tree) = new DocDef(comment, definition).copyAttrs(tree) def Template(tree: Tree, parents: List[Tree], self: ValDef, body: List[Tree]) = new Template(parents, self, body).copyAttrs(tree) @@ -1244,7 +1244,7 @@ trait Trees { if (expr0 == expr) && (selectors0 == selectors) => t case _ => treeCopy.Import(tree, expr, selectors) } - def DocDef(tree: Tree, comment: String, definition: Tree) = tree match { + def DocDef(tree: Tree, comment: DocComment, definition: Tree) = tree match { case t @ DocDef(comment0, definition0) if (comment0 == comment) && (definition0 == definition) => t case _ => treeCopy.DocDef(tree, comment, definition) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index de71ba30ab..c4c26e54b3 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -420,13 +420,13 @@ self => */ def joinComment(trees: => List[Tree]): List[Tree] = { val doc = in.flushDoc - if ((doc ne null) && doc._1.length > 0) { + if ((doc ne null) && doc.raw.length > 0) { val ts = trees val main = ts.find(_.pos.isOpaqueRange) ts map { t => - val dd = DocDef(doc._1, t) - val pos = doc._2.withEnd(t.pos.endOrPoint) + val dd = DocDef(doc, t) + val pos = doc.pos.withEnd(t.pos.endOrPoint) dd setPos (if (t eq main) pos else pos.makeTransparent) } } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index 62c47e8606..3de13ed877 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -103,11 +103,11 @@ trait Scanners { /** buffer for the documentation comment */ var docBuffer: StringBuilder = null - var docOffset: Position = null + var docPos: Position = null /** Return current docBuffer and set docBuffer to null */ - def flushDoc = { - val ret = if (docBuffer != null) (docBuffer.toString, docOffset) else null + def flushDoc: DocComment = { + val ret = if (docBuffer != null) DocComment(docBuffer.toString, docPos) else null docBuffer = null ret } @@ -1091,8 +1091,8 @@ trait Scanners { } override def foundDocComment(value: String, start: Int, end: Int) { - docOffset = new RangePosition(unit.source, start, start, end) - unit.comment(docOffset, value) + docPos = new RangePosition(unit.source, start, start, end) + unit.comment(docPos, value) } } diff --git a/src/compiler/scala/tools/nsc/doc/DocFactory.scala b/src/compiler/scala/tools/nsc/doc/DocFactory.scala index 240ba17d51..3c969c41f8 100644 --- a/src/compiler/scala/tools/nsc/doc/DocFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/DocFactory.scala @@ -39,7 +39,7 @@ class DocFactory(val reporter: Reporter, val settings: doc.Settings) { processor override def onlyPresentation = true lazy val addSourceless = { val sless = new SourcelessComments { val global = compiler } - comments ++= sless.comments + docComments ++= sless.comments } } diff --git a/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala b/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala index 50888ba27c..9216fa6f23 100644 --- a/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala +++ b/src/compiler/scala/tools/nsc/doc/SourcelessComments.scala @@ -21,9 +21,9 @@ abstract class SourcelessComments { lazy val comments = { - val comment = mutable.HashMap.empty[Symbol, String] + val comment = mutable.HashMap.empty[Symbol, DocComment] - comment(NothingClass) = """ + comment(NothingClass) = new DocComment(""" /** <p> * Class <code>Nothing</code> is - together with class <a href="Null.html"> * <code>Null</code></a> - at the bottom of the @@ -44,9 +44,9 @@ abstract class SourcelessComments { * instance of <code><a href="List.html">List</a>[T]</code>, for * any element type <code>T</code>. * </p> */ - """ + """) - comment(NullClass) = """ + comment(NullClass) = new DocComment(""" /** <p> * Class <code>Null</code> is - together with class <a href="Nothing.html"> * <code>Nothing</code> - at the bottom of the @@ -61,12 +61,12 @@ abstract class SourcelessComments { * it is not possible to assign <code>null</code> to a variable of * type <a href="Int.html"><code>Int</code></a>. * </p> */ - """ + """) /*******************************************************************/ /* Documentation for Any */ - comment(AnyClass) = """ + comment(AnyClass) = new DocComment(""" /** <p> * Class <code>Any</code> is the root of the <a * href="http://scala-lang.org/" @@ -77,9 +77,9 @@ abstract class SourcelessComments { * <a href="AnyRef.html"><code>AnyRef</code></a> and * <a href="AnyVal.html"><code>AnyVal</code></a>. * </p> */ - """ + """) - comment(Any_equals) = """ + comment(Any_equals) = new DocComment(""" /** This method is used to compare the receiver object (<code>this</code>) * with the argument object (<code>arg0</code>) for equivalence. * @@ -113,34 +113,34 @@ abstract class SourcelessComments { * @param arg0 the object to compare against this object for equality. * @return <code>true</code> if the receiver object is equivalent to the argument; <code>false</code> otherwise. * </p> */ - """ + """) - comment(Any_==) = """ + comment(Any_==) = new DocComment(""" /** `o == arg0` is the same as `o.equals(arg0)`. * <p> * @param arg0 the object to compare against this object for equality. * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. * </p> */ - """ + """) - comment(Any_!=) = """ + comment(Any_!=) = new DocComment(""" /** `o != arg0` is the same as `!(o == (arg0))`. * <p> * @param arg0 the object to compare against this object for dis-equality. * @return `false` if the receiver object is equivalent to the argument; `true` otherwise. * </p> */ - """ + """) - comment(Any_toString) = """ + comment(Any_toString) = new DocComment(""" /** Returns a string representation of the object. * <p> * The default representation is platform dependent. * * @return a string representation of the object. * </p>*/ - """ + """) - comment(Any_asInstanceOf) = """ + comment(Any_asInstanceOf) = new DocComment(""" /**This method is used to cast the receiver object to be of type <code>T0</code>. * * <p>Note that the success of a cast at runtime is modulo Scala's @@ -156,9 +156,9 @@ abstract class SourcelessComments { * instance of erasure of type <code>T0</code>. * @return the receiver object. * </p> */ - """ + """) - comment(Any_isInstanceOf) = """ + comment(Any_isInstanceOf) = new DocComment(""" /** This method is used to test whether the dynamic type of the receiver object is <code>T0</code>. * * <p>Note that the test result of the test is modulo Scala's erasure @@ -172,9 +172,9 @@ abstract class SourcelessComments { * * @return <code>true</code> if the receiver object is an * instance of erasure of type <code>T0</code>; <code>false</code> otherwise. */ - """ + """) - comment(Any_hashCode) = """ + comment(Any_hashCode) = new DocComment(""" /** Returns a hash code value for the object. * * <p> @@ -195,36 +195,36 @@ abstract class SourcelessComments { * <p> * @return the hash code value for the object. * </p> */ - """ + """) /*******************************************************************/ /* Documentation for AnyRef */ - comment(AnyRefClass) = """ + comment(AnyRefClass) = new DocComment(""" /** <p> * Class <code>AnyRef</code> is the root class of all * <em>reference types</em>. * </p> */ - """ + """) - comment(Object_==) = """ + comment(Object_==) = new DocComment(""" /** <code>o == arg0</code> is the same as <code>if (o eq null) arg0 eq null else o.equals(arg0)</code>. * <p> * @param arg0 the object to compare against this object for equality. * @return <code>true</code> if the receiver object is equivalent to the argument; <code>false</code> otherwise. * </p> */ - """ + """) - comment(Object_ne) = """ + comment(Object_ne) = new DocComment(""" /** <code>o.ne(arg0)</code> is the same as <code>!(o.eq(arg0))</code>. * <p> * @param arg0 the object to compare against this object for reference dis-equality. * @return <code>false</code> if the argument is not a reference to the receiver object; <code>true</code> otherwise. * </p> */ - """ + """) - comment(Object_finalize) = """ + comment(Object_finalize) = new DocComment(""" /** This method is called by the garbage collector on the receiver object when garbage * collection determines that there are no more references to the object. * <p> @@ -232,9 +232,9 @@ abstract class SourcelessComments { * invoked, as well as the interaction between <code>finalize</code> * and non-local returns and exceptions, are all platform dependent. * </p> */ - """ + """) - comment(Object_clone) = """ + comment(Object_clone) = new DocComment(""" /** This method creates and returns a copy of the receiver object. * * <p> @@ -242,9 +242,9 @@ abstract class SourcelessComments { * * @return a copy of the receiver object. * </p> */ - """ + """) - comment(Object_getClass) = """ + comment(Object_getClass) = new DocComment(""" /** Returns a representation that corresponds to the dynamic class of the receiver object. * * <p> @@ -252,17 +252,17 @@ abstract class SourcelessComments { * * @return a representation that corresponds to the dynamic class of the receiver object. * </p> */ - """ + """) - comment(Object_notify) = """ + comment(Object_notify) = new DocComment(""" /** Wakes up a single thread that is waiting on the receiver object's monitor. */ - """ + """) - comment(Object_notifyAll) = """ + comment(Object_notifyAll) = new DocComment(""" /** Wakes up all threads that are waiting on the receiver object's monitor. */ - """ + """) - comment(Object_eq) = """ + comment(Object_eq) = new DocComment(""" /** This method is used to test whether the argument (<code>arg0</code>) is a reference to the * receiver object (<code>this</code>). * @@ -304,11 +304,11 @@ abstract class SourcelessComments { * @param arg0 the object to compare against this object for reference equality. * @return <code>true</code> if the argument is a reference to the receiver object; <code>false</code> otherwise. * </p> */ - """ + """) /*******************************************************************/ - comment(AnyValClass) = """ + comment(AnyValClass) = new DocComment(""" /** <p> * Class <code>AnyVal</code> is the root class of all * <em>value types</em>. @@ -338,25 +338,25 @@ abstract class SourcelessComments { * <a href="Double.html"><code>Double</code></a> are called * <em>floating point types</em>. * </p> */ - """ + """) - comment(BooleanClass) = """ + comment(BooleanClass) = new DocComment(""" /** <p> * Class <code>Boolean</code> has only two values: <code>true</code> * and <code>false</code>. * </p> */ - """ + """) - comment(UnitClass) = """ + comment(UnitClass) = new DocComment(""" /** <p> * Class <code>Unit</code> has only one value: <code>()</code>. * </p> */ - """ + """) List(ByteClass, CharClass, DoubleClass, LongClass, FloatClass, IntClass, ShortClass) foreach { sym => val maxValue = "MAX_" + sym.name.toString().toUpperCase() val minValue = "MIN_" + sym.name.toString().toUpperCase() - comment(sym) = """ + comment(sym) = new DocComment(""" /** <p> * Class <code>""" + sym.name + """</code> belongs to the value * classes whose instances are not represented as objects by the @@ -370,7 +370,7 @@ abstract class SourcelessComments { * Values <code>""" + maxValue + """</code> and <code>""" + minValue + """</code> * are in defined in object <a href="Math$object.html">scala.Math</a>. * </p> */ - """ + """) } comment diff --git a/src/compiler/scala/tools/nsc/interactive/Global.scala b/src/compiler/scala/tools/nsc/interactive/Global.scala index b8a219fb4d..35d212071e 100644 --- a/src/compiler/scala/tools/nsc/interactive/Global.scala +++ b/src/compiler/scala/tools/nsc/interactive/Global.scala @@ -512,11 +512,8 @@ self => */ private def applyPhase(phase: Phase, unit: CompilationUnit) { val oldSource = reporter.getSource - try { - reporter.setSource(unit.source) + reporter.withSource(unit.source) { atPhase(phase) { phase.asInstanceOf[GlobalPhase] applyPhase unit } - } finally { - reporter setSource oldSource } } } diff --git a/src/compiler/scala/tools/nsc/reporters/Reporter.scala b/src/compiler/scala/tools/nsc/reporters/Reporter.scala index b00590d501..99292f2338 100644 --- a/src/compiler/scala/tools/nsc/reporters/Reporter.scala +++ b/src/compiler/scala/tools/nsc/reporters/Reporter.scala @@ -41,6 +41,15 @@ abstract class Reporter { private var source: SourceFile = _ def setSource(source: SourceFile) { this.source = source } def getSource: SourceFile = source + def withSource[A](src: SourceFile)(op: => A) = { + val oldSource = source + try { + source = src + op + } finally { + source = oldSource + } + } def info(pos: Position, msg: String, force: Boolean) { info0(pos, msg, INFO, force) } def warning(pos: Position, msg: String ) { info0(pos, msg, WARNING, false) } diff --git a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala index da0d237567..2a70c55cd6 100644 --- a/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala +++ b/src/compiler/scala/tools/nsc/symtab/SymbolTable.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package symtab -import ast.Trees +import ast.{Trees, DocComments} import util._ @@ -23,6 +23,7 @@ abstract class SymbolTable extends Names with AnnotationCheckers with Trees with Positions + with DocComments { def settings: Settings def rootLoader: LazyType diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala index 8d3d0bbf2a..ee0576d654 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala @@ -538,7 +538,7 @@ abstract class UnPickler { case other => errorBadSignature("Document comment not a string (" + other + ")") } val definition = readTreeRef() - DocDef(comment, definition) + DocDef(DocComment(comment, NoPosition), definition) case TEMPLATEtree => setSym() diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 1e8b93bf5d..2132be4d50 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -758,9 +758,6 @@ trait Namers { self: Analyzer => tpt setPos meth.pos.focus } - if (onlyPresentation && methodArgumentNames != null) - methodArgumentNames(meth) = vparamss.map(_.map(_.symbol)); - def convertToDeBruijn(vparams: List[Symbol], level: Int): TypeMap = new TypeMap { def debruijnFor(param: Symbol) = DeBruijnIndex(level, vparams indexOf param) diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index 682a7db991..a18fbe70d1 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -151,6 +151,17 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT decls.enter(s) } } + if (settings.verbose.value && onlyPresentation && !sym.isAnonymousClass) { + println("========== scaladoc of "+sym+" =============================") + for (member <- sym.info.members) { + println(member+":"+sym.thisType.memberInfo(member)+"\n"+ + cookedDocComment(member, sym)) + for ((useCase, comment) <- useCases(member, sym)) { + println("usecase "+useCase+":"+useCase.info) + println(comment) + } + } + } super.transform(tree) case ModuleDef(_, _, _) => checkCompanionNameClashes(sym) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7e4d6b7d7d..a610f85104 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -15,7 +15,7 @@ import scala.collection.mutable.{HashMap, ListBuffer} import scala.util.control.ControlException import scala.compat.Platform.currentTime import scala.tools.nsc.interactive.RangePositions -import scala.tools.nsc.util.{ Position, Set, NoPosition, SourceFile } +import scala.tools.nsc.util.{ Position, Set, NoPosition, SourceFile, BatchSourceFile } import symtab.Flags._ // Suggestion check whether we can do without priming scopes with symbols of outer scopes, @@ -689,8 +689,8 @@ trait Typers { self: Analyzer => else qual.tpe.nonLocalMember(name) } - def silent(op: Typer => Tree): AnyRef /* in fact, TypeError or Tree */ = { - val start = System.nanoTime() + def silent[T](op: Typer => T): Any /* in fact, TypeError or T */ = { +// val start = System.nanoTime() try { if (context.reportGeneralErrors) { val context1 = context.makeSilent(context.reportAmbiguousErrors) @@ -709,7 +709,7 @@ trait Typers { self: Analyzer => } catch { case ex: CyclicReference => throw ex case ex: TypeError => - failedSilent += System.nanoTime() - start +// failedSilent += System.nanoTime() - start ex }} @@ -1709,6 +1709,45 @@ trait Typers { self: Analyzer => } } + def typedUseCase(useCase: UseCase) { + def stringParser(str: String): syntaxAnalyzer.Parser = { + val file = new BatchSourceFile(context.unit.source.file, str) { + override def positionInUltimateSource(pos: Position) = { + pos.withSource(context.unit.source, useCase.pos.start) + } + } + val unit = new CompilationUnit(file) + new syntaxAnalyzer.UnitParser(unit) + } + val trees = stringParser(useCase.body+";").nonLocalDefOrDcl + val enclClass = context.enclClass.owner + def defineAlias(name: Name) = + if (context.scope.lookup(name) == NoSymbol) { + lookupVariable(name.toString.substring(1), enclClass) match { + case Some(repl) => + silent(_.typedTypeConstructor(stringParser(repl).typ())) match { + case tpt: Tree => + val alias = enclClass.newAliasType(useCase.pos, name) + val tparams = cloneSymbols(tpt.tpe.typeSymbol.typeParams, alias) + alias setInfo polyType(tparams, appliedType(tpt.tpe, tparams map (_.tpe))) + context.scope.enter(alias) + case _ => + } + case _ => + } + } + for (tree <- trees; t <- tree) + t match { + case Ident(name) if (name.length > 0 && name(0) == '$') => defineAlias(name) + case _ => + } + useCase.aliases = context.scope.toList + namer.enterSyms(trees) + typedStats(trees, NoSymbol) + useCase.defined = context.scope.toList -- useCase.aliases +// println("defined use cases: "+(useCase.defined map (sym => sym+":"+sym.tpe))) + } + /** * @param ddef ... * @return ... @@ -3701,12 +3740,18 @@ trait Typers { self: Analyzer => labelTyper(ldef).typedLabelDef(ldef) case ddef @ DocDef(comment, defn) => - val ret = typed(defn, mode, pt) - if ((comments ne null) && (defn.symbol ne null) && (defn.symbol ne NoSymbol)) { - comments(defn.symbol) = comment - commentOffsets(defn.symbol) = ddef.pos.startOrPoint + if (onlyPresentation && (sym ne null) && (sym ne NoSymbol)) { + docComments(sym) = comment + comment.defineVariables(sym) + val typer1 = newTyper(context.makeNewScope(tree, context.owner)) + for (useCase <- comment.useCases) + typer1.silent(_.typedUseCase(useCase)) match { + case ex: TypeError => + unit.warning(useCase.pos, ex.msg) + case _ => + } } - ret + typed(defn, mode, pt) case Annotated(constr, arg) => typedAnnotated(constr, typed(arg, mode, pt)) diff --git a/src/compiler/scala/tools/nsc/util/Position.scala b/src/compiler/scala/tools/nsc/util/Position.scala index 999dca3927..806c885291 100644 --- a/src/compiler/scala/tools/nsc/util/Position.scala +++ b/src/compiler/scala/tools/nsc/util/Position.scala @@ -109,6 +109,9 @@ trait Position { /** The same position with a different point value (if a range or offset) */ def withPoint(off: Int) = this + /** The same position with a different source value, and its values shifted by given offset */ + def withSource(source: SourceFile, shift: Int) = this + /** If this is a range, the union with the other range, with the point of this position. * Otherwise, this position */ @@ -200,6 +203,7 @@ class OffsetPosition(override val source: SourceFile, override val point: Int) e override def isDefined = true override def pointOrElse(default: Int): Int = point override def withPoint(off: Int) = new OffsetPosition(source, off) + override def withSource(source: SourceFile, shift: Int) = new OffsetPosition(source, point + shift) override def line: Int = source.offsetToLine(point) + 1 @@ -240,6 +244,7 @@ extends OffsetPosition(source, point) { override def withStart(off: Int) = new RangePosition(source, off, point, end) override def withEnd(off: Int) = new RangePosition(source, start, point, off) override def withPoint(off: Int) = new RangePosition(source, start, off, end) + override def withSource(source: SourceFile, shift: Int) = new RangePosition(source, start + shift, point + shift, end + shift) override def focusStart = new OffsetPosition(source, start) override def focus = { if (focusCache eq NoPosition) focusCache = new OffsetPosition(source, point) diff --git a/src/library/scala/collection/IndexedSeqLike.scala b/src/library/scala/collection/IndexedSeqLike.scala index 68862e4aed..7041078229 100644 --- a/src/library/scala/collection/IndexedSeqLike.scala +++ b/src/library/scala/collection/IndexedSeqLike.scala @@ -77,7 +77,18 @@ trait IndexedSeqLike[+A, +Repr] extends SeqLike[A, Repr] { self => val i = prefixLength(!p(_)) if (i < length) Some(this(i)) else None } - +/* + override def mapFind[B](f: A => Option[B]): Option[B] = { + var i = 0 + var res: Option[B] = None + val len = length + while (res.isEmpty && i < len) { + res = f(this(i)) + i += 1 + } + res + } +*/ @tailrec private def foldl[B](start: Int, end: Int, z: B, op: (B, A) => B): B = if (start == end) z diff --git a/src/library/scala/collection/IterableLike.scala b/src/library/scala/collection/IterableLike.scala index f1401f1221..66f63dd2f5 100644 --- a/src/library/scala/collection/IterableLike.scala +++ b/src/library/scala/collection/IterableLike.scala @@ -14,7 +14,7 @@ import immutable.{List, Stream} import annotation.unchecked.uncheckedVariance /** <p> - * A template trait for iterable collections. + * A template trait for iterable collections of type `Iterable[A]`. * </p> * <p> * Collection classes mixing in this trait provide a method @@ -36,11 +36,27 @@ import annotation.unchecked.uncheckedVariance * <code>Iterable</code></a>. * </p> * - * @note This trait replaces every method that uses breaks in the original by an iterator version. + * Note: This trait replaces every method that uses breaks in the original by an iterator version. + * + * @see Iterable * * @author Martin Odersky * @version 2.8 * @since 2.8 + * + * @tparam A the element type of the collection + * @tparam Repr the type of the actual collection containing the elements. + * + * @define Coll Iterable + * @define coll iterable collection + * @define zipthatinfo the class of the returned collection. Where possible, `That` is + * the same class as the current collection class `Repr`, but this + * depends on the element type `(A1, B)` being admissible for that class, + * which means that an implicit instance of type `CanBuildFrom[Repr, (A1, B), That]`. + * is found. + * @define zipbfinfo an implicit value of class `CanBuildFrom` which determines the + * result class `That` from the current representation type `Repr` + * and the new element type `(A1, B)`. */ trait IterableLike[+A, +Repr] extends Equals with TraversableLike[A, Repr] { self => @@ -58,102 +74,50 @@ self => @deprecated("use `iterator' instead") def elements = iterator - /** Apply a function <code>f</code> to all elements of this - * iterable object. - * - * @param f A function that is applied for its side-effect to every element. - * The result (of arbitrary type U) of function `f` is discarded. - * - * @note This method underlies the implementation of most other bulk operations. - * Implementing `foreach` with `iterator` is often suboptimal. - * So `foreach` should be overridden in concrete collection classes if a more - * efficient implementation is available. - */ - def foreach[U](f: A => U): Unit = iterator.foreach(f) - - - /** Return true iff the given predicate `p` yields true for all elements - * of this iterable. - * - * @note May not terminate for infinite-sized collections. - * @param p the predicate - */ - override def forall(p: A => Boolean): Boolean = iterator.forall(p) - - /** Return true iff there is an element in this iterable for which the - * given predicate `p` yields true. - * - * @note May not terminate for infinite-sized collections. - * @param p the predicate - */ - override def exists(p: A => Boolean): Boolean = iterator.exists(p) - - /** Find and return the first element of the iterable object satisfying a - * predicate, if any. + /** Applies a function `f` to all elements of this $coll. * - * @note may not terminate for infinite-sized collections. - * @note Might return different results for different runs, unless this iterable is ordered. - * @param p the predicate - * @return an option containing the first element in the iterable object - * satisfying <code>p</code>, or <code>None</code> if none exists. - */ - override def find(p: A => Boolean): Option[A] = iterator.find(p) - - /** Does this iterable contain no elements? - */ - override def isEmpty: Boolean = !this.iterator.hasNext - - /** Combines the elements of this iterable together using the binary - * function <code>f</code>, from right to left, and starting with - * the value <code>z</code>. + * Note: this method underlies the implementation of most other bulk operations. + * Subclasses should re-implement this method if a more efficient implementation exists. * - * @note Will not terminate for infinite-sized collections. - * @note Might return different results for different runs, unless this iterable is ordered, or - * the operator is associative and commutative. - * @return <code>f(a<sub>0</sub>, f(a<sub>1</sub>, f(..., f(a<sub>n</sub>, z)...)))</code> - * if the iterable is <code>[a<sub>0</sub>, a1, ..., a<sub>n</sub>]</code>. - */ - override def foldRight[B](z: B)(op: (A, B) => B): B = - this.iterator.foldRight(z)(op) - - /** Combines the elements of this iterable object together using the binary - * operator <code>op</code>, from right to left - * @note Will not terminate for infinite-sized collections. - * @note Might return different results for different runs, unless this iterable is ordered, or - * the operator is associative and commutative. - * @param op The operator to apply + * @param f the function that is applied for its side-effect to every element. + * The result of function `f` is discarded. * - * @return <code>a<sub>0</sub> op (... op (a<sub>n-1</sub> op a<sub>n</sub>)...)</code> - * if the iterable object has elements <code>a<sub>0</sub>, a<sub>1</sub>, ..., - * a<sub>n</sub></code>. + * @tparam U the type parameter describing the result of function `f`. + * This result will always be ignored. Typically `U` is `Unit`, + * but this is not necessary. * - * @throws Predef.UnsupportedOperationException if the iterator is empty. - */ - override def reduceRight[B >: A](op: (A, B) => B): B = - this.iterator.reduceRight(op) - - /** The iterable itself */ - override def toIterable: Iterable[A] = thisCollection - - /** The first element of this iterable. + * @usecase def foreach(f: A => Unit): Unit * - * @note Might return different results for different runs, unless this iterable is ordered - * @throws Predef.NoSuchElementException if the iterable is empty. + * @param f the function that is applied for its side-effect to every element. + * The result of function `f` is discarded. */ - override def head: A = - if (isEmpty) - throw new NoSuchElementException - else - this.iterator.next - - /** Return an iterable consisting only of the first <code>n</code> - * elements of this iterable, or else the whole iterable, if it has less - * than <code>n</code> elements. - * - * @param n the number of elements to take - * @note Might return different results for different runs, unless this iterable is ordered - */ - override def take(n: Int): Repr = { + def foreach[U](f: A => U): Unit = + iterator.foreach(f) + + override /*TraversableLike*/ def forall(p: A => Boolean): Boolean = + iterator.forall(p) + override /*TraversableLike*/ def exists(p: A => Boolean): Boolean = + iterator.exists(p) + override /*TraversableLike*/ def find(p: A => Boolean): Option[A] = + iterator.find(p) +/* + override /*TraversableLike*/ def mapFind[B](f: A => Option[B]): Option[B] = + iterator.mapFind(f) +*/ + override /*TraversableLike*/ def isEmpty: Boolean = + !iterator.hasNext + override /*TraversableLike*/ def foldRight[B](z: B)(op: (A, B) => B): B = + iterator.foldRight(z)(op) + override /*TraversableLike*/ def reduceRight[B >: A](op: (A, B) => B): B = + iterator.reduceRight(op) + override /*TraversableLike*/ def toIterable: Iterable[A] = + thisCollection + + override /*TraversableLike*/ def head: A = + if (isEmpty) throw new NoSuchElementException + else iterator.next + + override /*TraversableLike*/ def take(n: Int): Repr = { val b = newBuilder var i = 0 val it = iterator @@ -164,17 +128,7 @@ self => b.result } - /** A sub-iterable starting at index `from` - * and extending up to (but not including) index `until`. - * - * @note c.slice(from, to) is equivalent to (but possibly more efficient than) - * c.drop(from).take(to - from) - * - * @param from The index of the first element of the returned subsequence - * @param until The index of the element following the returned subsequence - * @note Might return different results for different runs, unless this iterable is ordered - */ - override def slice(from: Int, until: Int): Repr = { + override /*TraversableLike*/ def slice(from: Int, until: Int): Repr = { val b = newBuilder var i = from val it = iterator drop from @@ -185,13 +139,7 @@ self => b.result } - /** Returns the longest prefix of this iterable whose elements satisfy - * the predicate <code>p</code>. - * - * @param p the test predicate. - * @note Might return different results for different runs, unless this iterable is ordered - */ - override def takeWhile(p: A => Boolean): Repr = { + override /*TraversableLike*/ def takeWhile(p: A => Boolean): Repr = { val b = newBuilder val it = iterator while (it.hasNext) { @@ -202,10 +150,12 @@ self => b.result } - /** Returns the rightmost <code>n</code> elements from this iterable. + /** Selects last ''n'' elements. + * $orderDependent * * @param n the number of elements to take - * @note Might return different results for different runs, unless this iterable is ordered + * @return a $coll consisting only of the last `n` elements of this $coll, or else the + * whole $coll, if it has less than `n` elements. */ def takeRight(n: Int): Repr = { val b = newBuilder @@ -219,10 +169,12 @@ self => b.result } - /** Returns the iterable wihtout its rightmost <code>n</code> elements. + /** Selects all elements except first ''n'' ones. + * $orderDependent * - * @param n the number of elements to take - * @note Might return different results for different runs, unless this iterable is ordered + * @param n The number of elements to take + * @return a $coll consisting of all elements of this $coll except the first `n` ones, or else the + * empty $coll, if this $coll has less than `n` elements. */ def dropRight(n: Int): Repr = { val b = newBuilder @@ -235,17 +187,7 @@ self => b.result } - /** Fills the given array <code>xs</code> with at most `len` elements of - * this iterable starting at position `start`. - * Copying will stop once either the end of the current iterable is reached or - * `len` elements have been copied or the end of the array is reached. - * - * @note Will not terminate for infinite-sized collections. - * @param xs the array to fill. - * @param start starting index. - * @param len number of elements to copy - */ - override def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { + override /*TraversableLike*/ def copyToArray[B >: A](xs: Array[B], start: Int, len: Int) { var i = start val end = (start + len) min xs.length val it = iterator @@ -255,10 +197,29 @@ self => } } - /** Returns an iterable formed from this iterable and another iterable + /** Returns a $coll formed from this $coll and another iterable collection * by combining corresponding elements in pairs. - * If one of the two iterables is longer than the other, its remaining elements are ignored. + * If one of the two collections is longer than the other, its remaining elements are ignored. + * + * $orderDependent + * * @param that The iterable providing the second half of each result pair + * @tparam A1 the type of the first half of the returned pairs (this is always a supertype + * of the collection's element type `A`). + * @tparam B the type of the second half of the returned pairs + * @tparam That $zipthatinfo + * @param bf $zipbfinfo + * @return a new collection of type `That` containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the minimum of the lengths of this $coll$ and `that`. + * + * @usecase def zip[B](that: Iterable[B]): $Coll[(A, B)] + * + * @param that The iterable providing the second half of each result pair + * @tparam B the type of the second half of the returned pairs + * @return a new $coll containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the minimum of the lengths of this $coll$ and `that`. */ def zip[A1 >: A, B, That](that: Iterable[B])(implicit bf: CanBuildFrom[Repr, (A1, B), That]): That = { val b = bf(repr) @@ -269,25 +230,33 @@ self => b.result } - /** Returns an iterable formed from this iterable and the specified iterable - * <code>that</code> by associating each element of the former with - * the element at the same position in the latter. + /** Returns a $coll formed from this $coll and another iterable collection + * by combining corresponding elements in pairs. + * If one of the two collections is shorter than the other, + * placeholder elements are used to extend the collection to the longer length. * - * @param that iterable <code>that</code> may have a different length - * as the self iterable. - * @param thisElem element <code>thisElem</code> is used to fill up the - * resulting iterable if the self iterable is shorter than - * <code>that</code> - * @param thatElem element <code>thatElem</code> is used to fill up the - * resulting iterable if <code>that</code> is shorter than - * the self iterable - * @return <code>Sequence((a<sub>0</sub>,b<sub>0</sub>), ..., - * (a<sub>n</sub>,b<sub>n</sub>), (elem,b<sub>n+1</sub>), - * ..., {elem,b<sub>m</sub>})</code> - * when <code>[a<sub>0</sub>, ..., a<sub>n</sub>] zip - * [b<sub>0</sub>, ..., b<sub>m</sub>]</code> is - * invoked where <code>m > n</code>. + * $orderDependent * + * @param that the iterable providing the second half of each result pair + * @param thisElem the element to be used to fill up the result if this $coll is shorter than `that`. + * @param thatElem the element to be used to fill up the result if `that` is shorter than this $coll. + * @return a new collection of type `That` containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the maximum of the lengths of this $coll$ and `that`. + * If this $coll is shorter than `that`, `thisElem` values are used to pad the result. + * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. + * + * @usecase def zipAll[B](that: Iterable[B], thisElem: A, thatElem: B): $Coll[(A, B)] + * + * @param that The iterable providing the second half of each result pair + * @param thisElem the element to be used to fill up the result if this $coll is shorter than `that`. + * @param thatElem the element to be used to fill up the result if `that` is shorter than this $coll. + * @tparam B the type of the second half of the returned pairs + * @return a new $coll containing pairs consisting of + * corresponding elements of this $coll and `that`. The length + * of the returned collection is the maximum of the lengths of this $coll$ and `that`. + * If this $coll is shorter than `that`, `thisElem` values are used to pad the result. + * If `that` is shorter than this $coll, `thatElem` values are used to pad the result. */ def zipAll[B, A1 >: A, That](that: Iterable[B], thisElem: A1, thatElem: B)(implicit bf: CanBuildFrom[Repr, (A1, B), That]): That = { val b = bf(repr) @@ -302,7 +271,30 @@ self => b.result } - /** Zips this iterable with its indices (startiong from 0). + /** Zips this $coll with its indices. + * + * $orderDependent + * + * @tparam A1 the type of the first half of the returned pairs (this is always a supertype + * of the collection's element type `A`). + * @tparam That the class of the returned collection. Where possible, `That` is + * the same class as the current collection class `Repr`, but this + * depends on the element type `(A1, Int)` being admissible for that class, + * which means that an implicit instance of type `CanBuildFrom[Repr, (A1, Int), That]`. + * is found. + * @tparam bf an implicit value of class `CanBuildFrom` which determines the + * result class `That` from the current representation type `Repr` + * and the new element type `(A1, Int)`. + * @return A new collection of type `That` containing pairs consisting of all elements of this + * $coll paired with their index. Indices start at `0`. + * + * @usecase def zipWithIndex: $Coll[(A, Int)] + * + * @return A new $coll containing pairs consisting of all elements of this + * $coll paired with their index. Indices start at `0`. + * @example + * `List("a", "b", "c").zipWithIndex = List(("a", 0), ("b", 1), ("c", 2))` + * */ def zipWithIndex[A1 >: A, That](implicit bf: CanBuildFrom[Repr, (A1, Int), That]): That = { val b = bf(repr) @@ -314,12 +306,19 @@ self => b.result } - /** Checks if the other iterable object contains the same elements as this one. + /** Checks if the other iterable collection contains the same elements in the same order as this $coll. + * + * $orderDependent + * $willNotTerminateInf + * + * @param that the collection to compare with. + * @tparam B the type of the elements of collection `that`. + * @return `true`, if both collections contain the same elements in the same order, `false` otherwise. * - * @note will not terminate for infinite-sized iterables. - * @param that the other iterable - * @return true, iff both iterables contain the same elements in the same order. - * @note Might return different results for different runs, unless this iterable is ordered + * @usecase def sameElements(that: Iterable[A]): Boolean + * + * @param that the collection to compare with. + * @return `true`, if both collections contain the same elements in the same order, `false` otherwise. */ def sameElements[B >: A](that: Iterable[B]): Boolean = { val these = this.iterator @@ -331,44 +330,36 @@ self => !these.hasNext && !those.hasNext } - /** Returns a stream with all elements in this iterable object. - */ - override def toStream: Stream[A] = iterator.toStream + override /*TraversableLike*/ def toStream: Stream[A] = iterator.toStream /** Method called from equality methods, so that user-defined subclasses can * refuse to be equal to other collections of the same kind. + * @param that The object with which this $coll should be compared + * @return `true`, if this $coll can possibly equal `that`, `false` otherwise. The test + * takes into consideration only the run-time types of objects but ignores their elements. */ - override def canEqual(that: Any) = true + override /*TraversableLike*/ def canEqual(that: Any) = true - /** Creates a view of this iterable @see IterableView - */ - override def view = new IterableView[A, Repr] { + override /*TraversableLike*/ def view = new IterableView[A, Repr] { protected lazy val underlying = self.repr override def iterator = self.iterator } - /** A sub-iterable view starting at index `from` - * and extending up to (but not including) index `until`. - * - * @param from The index of the first element of the slice - * @param until The index of the element following the slice - * @note The difference between `view` and `slice` is that `view` produces - * a view of the current iterable, whereas `slice` produces a new iterable. - * - * @note Might return different results for different runs, unless this iterable is ordered - * @note view(from, to) is equivalent to view.slice(from, to) - */ - override def view(from: Int, until: Int) = view.slice(from, until) + override /*TraversableLike*/ def view(from: Int, until: Int) = view.slice(from, until) + /** @deprecated use `head' instead. */ @deprecated("use `head' instead") def first: A = head - /** <code>None</code> if iterable is empty. */ + /** <code>None</code> if iterable is empty. + * @deprecated "use `headOption' instead" + */ @deprecated("use `headOption' instead") def firstOption: Option[A] = headOption /** * returns a projection that can be used to call non-strict <code>filter</code>, * <code>map</code>, and <code>flatMap</code> methods that build projections * of the collection. + * @deprecated "use `view' instead" */ @deprecated("use `view' instead") def projection = view diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index fa5c2d3597..ac6cb890c4 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -636,6 +636,21 @@ trait Iterator[+A] { self => res } + /** Applies option-valued function to successive elements of this iterator + * until a defined value is found. + * + * @param f the function to be applied to successive elements. + * @return an option value containing the first defined result of + * `f`, or `None` if `f` returns `None` for all all elements. + def mapFind[B](f: A => Option[B]): Option[B] = { + var res: Option[B] = None + while (res.isEmpty && hasNext) { + res = f(next()) + } + res + } + */ + /** Returns index of the first element satisfying a predicate, or -1. * * @note may not terminate for infinite-sized collections. diff --git a/src/library/scala/collection/LinearSeqLike.scala b/src/library/scala/collection/LinearSeqLike.scala index 4fb9b4ad79..08bb071da9 100644 --- a/src/library/scala/collection/LinearSeqLike.scala +++ b/src/library/scala/collection/LinearSeqLike.scala @@ -152,7 +152,17 @@ trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] extends SeqLike[A, Repr } None } - +/* + override def mapFind[B](f: A => Option[B]): Option[B] = { + var res: Option[B] = None + var these = this + while (res.isEmpty && !these.isEmpty) { + res = f(these.head) + these = these.tail + } + res + } +*/ /** Combines the elements of this list together using the binary * function <code>f</code>, from left to right, and starting with * the value <code>z</code>. diff --git a/src/library/scala/collection/MapLike.scala b/src/library/scala/collection/MapLike.scala index a1ea657553..a9ea82b2f9 100644 --- a/src/library/scala/collection/MapLike.scala +++ b/src/library/scala/collection/MapLike.scala @@ -14,28 +14,30 @@ import generic._ import mutable.{Builder, StringBuilder, MapBuilder} import PartialFunction._ -/** <p> - * A generic template for maps from keys of type <code>A</code> to values - * of type <code>B</code>.<br/> - * To implement a concrete map, you need to provide implementations of the - * following methods (where <code>This</code> is the type of the map in +/** + * A generic template for maps from keys of type `A` to values + * of type `B`. + * + * To implement a concrete map, 'you' need to provide implementations of the + * following methods (where `This` is the type of the map in * question): - * </p> - * <pre> - * <b>def</b> get(key: A): Option[B] - * <b>def</b> iterator: Iterator[(A, B)] - * <b>def</b> + [B1 >: B](kv: (A, B1)): This - * <b>def</b> -(key: A): This</pre> - * <p> + * {{{ + * def get(key: A): Option[B] + * + * def iterator: Iterator[(A, B)] + * + * def + [B1 >: B](kv: (A, B1)): This + * + * def -(key: A): This</pre> + * }}} * If you wish that methods <code>like</code>, <code>take</code>, <code>drop</code>, * <code>filter</code> return the same kind of map, you should also override: - * </p> - * <pre> - * <b>def</b> empty: This</pre> - * <p> - * It is also good idea to override methods <code>foreach</code> and - * <code>size</code> for efficiency. - * </p> + * {{{ + * def empty: This + * }}} + * + * It is also good idea to override methods `foreach` and + * `size` for efficiency. * * @author Martin Odersky * @version 2.8 diff --git a/src/library/scala/collection/SeqLike.scala b/src/library/scala/collection/SeqLike.scala index 4d096a88c6..5e7ea8ebfa 100644 --- a/src/library/scala/collection/SeqLike.scala +++ b/src/library/scala/collection/SeqLike.scala @@ -10,20 +10,19 @@ package scala.collection -import generic._ + import mutable.{ListBuffer, HashMap, GenericArray} import immutable.{List, Range} - -// import immutable.{List, Nil, ::} import generic._ -/** Contains a KMP implementation, based on the undoubtedly reliable wikipedia entry. - * - * @author paulp - * @since 2.8 +/** The companion object for trait `SeqLike`. */ object SeqLike { + /** A KMP implementation, based on the undoubtedly reliable wikipedia entry. + * @author paulp + * @since 2.8 + */ private def KMP[B](S: Seq[B], W: Seq[B]): Option[Int] = { // trivial cases if (W.isEmpty) return Some(0) @@ -73,6 +72,8 @@ object SeqLike { None } + /** Waiting for a doc comment from Paul + */ def indexOf[B]( source: Seq[B], sourceOffset: Int, sourceCount: Int, target: Seq[B], targetOffset: Int, targetCount: Int, @@ -82,6 +83,8 @@ object SeqLike { case Some(x) => x + fromIndex } + /** Waiting for a doc comment from Paul + */ def lastIndexOf[B]( source: Seq[B], sourceOffset: Int, sourceCount: Int, target: Seq[B], targetOffset: Int, targetCount: Int, @@ -96,41 +99,89 @@ object SeqLike { } } -/** Class <code>Seq[A]</code> represents sequences of elements - * of type <code>A</code>. - * It adds the following methods to class Iterable: - * `length`, `lengthCompare`, `apply`, `isDefinedAt`, `segmentLength`, `prefixLength`, - * `indexWhere`, `indexOf`, `lastIndexWhere`, `lastIndexOf`, `reverse`, `reverseIterator`, - * `startsWith`, `endsWith`, `indexOfSlice`, , `zip`, `zipAll`, `zipWithIndex`. +/** A template trait for sequences of type `Seq[A]`, representing + * sequences of elements of type <code>A</code>. + * + * Sequences are special cases of iterable collections of class `Iterable`. + * Unlike iterables, sequences always have a defined order of elements. + * Sequences provide a method `apply` for indexing. Indices range from `0` up the the `length` of + * a sequence. Sequences support a number to find occurrences of elements or subsequences, including + * `segmentLength`, `prefixLength`, `indexWhere`, `indexOf`, `lastIndexWhere`, `lastIndexOf`, + * `startsWith`, `endsWith`, `indexOfSlice`. * + * Another way to see a sequence is as a `PartialFunction` from `Int` values + * to the element type of the sequence. The `isDefinedAt` method of a sequence + * returns `true` for the interval from `0` until `length`. + * + * Sequences can be accessed in reverse order of their elements, using methods + * `reverse` and `reverseIterator`. + * + * Sequences have two principle subtraits, `IndexedSeq` and `LinearSeq`, which give different guarantees for performance. + * An `IndexedSeq` provides fast random-access of elements and a fast `length` operation. + * A `LinearSeq` provides fast access only to the first element via `head`, but also + * has a fast `tail` operation. * * @author Martin Odersky * @author Matthias Zenger * @version 1.0, 16/07/2003 * @since 2.8 + * + * @tparam A the element type of the collection + * @tparam Repr the type of the actual collection containing the elements. + * + * @define Coll Seq + * @define coll sequence + * @define thatinfo the class of the returned collection. Where possible, `That` is + * the same class as the current collection class `Repr`, but this + * depends on the element type `B` being admissible for that class, + * which means that an implicit instance of type `CanBuildFrom[Repr, B, That]` + * is found. + * @define bfinfo an implicit value of class `CanBuildFrom` which determines the + * result class `That` from the current representation type `Repr` + * and the new element type `B`. + * @define orderDependent + * @define orderDependentFold + * @define mayNotTerminateInf + * + * Note: may not terminate for infinite-sized collections. + * @define willNotTerminateInf + * + * Note: will not terminate for infinite-sized collections. */ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => override protected[this] def thisCollection: Seq[A] = this.asInstanceOf[Seq[A]] override protected[this] def toCollection(repr: Repr): Seq[A] = repr.asInstanceOf[Seq[A]] - /** Returns the length of the sequence. + /** The length of the $coll. + * + * $willNotTerminateInf + * + * Note: `xs.length` and `xs.size` yield the same result. + * + * @return the number of elements in this $coll. */ def length: Int - /** Returns the elements at position `idx` + /** Selects an element by its index in the $coll + * + * @param idx The index to select + * @return the element of tyh */ def apply(idx: Int): A - /** Result of comparing <code>length</code> with operand <code>len</code>. - * returns <code>x</code> where - * <code>x < 0</code> iff <code>this.length < len</code> - * <code>x == 0</code> iff <code>this.length == len</code> - * <code>x > 0</code> iff <code>this.length > len</code>. + /** Compares the length of this $coll to a test value. * - * The method as implemented here does not call length directly; its running time - * is O(length min len) instead of O(length). The method should be overwritten - * if computing length is cheap. + * @param len the test value that gets compared with the length. + * @return A value `x` where + * {{{ + * x < 0 if this.length < len + * x == 0 if this.length == len + * x > 0 if this.length > len + * }}} + * The method as implemented here does not call `length` directly; its running time + * is `O(length min len)` instead of `O(length)`. The method should be overwritten + * if computing `length` is cheap. */ def lengthCompare(len: Int): Int = { var i = 0 @@ -142,18 +193,32 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => i - len } - /** Should always be <code>length</code> */ + /** The size of this $coll, equivalent to `length`. + * + * $willNotTerminateInf + * + * @return the number of elements in this $coll. + */ override def size = length - /** Is this partial function defined for the index <code>x</code>? + /** Tests whether this $coll contains given index. + * + * The implementations of methods `apply` and `isDefinedAt` turn a `Seq[A]` into + * a `PartialFunction[Int, A]`. + * + * @param idx the index to test + * @return `true` if this $coll contains an element at position `idx`, `false` otherwise. */ - def isDefinedAt(x: Int): Boolean = (x >= 0) && (x < length) + def isDefinedAt(idx: Int): Boolean = (idx >= 0) && (idx < length) - /** Returns length of longest segment starting from a start index `from` - * such that every element of the segment satisfies predicate `p`. - * @note may not terminate for infinite-sized collections. - * @param p the predicate - * @param from the start index + /** Computes length of longest segment whose elements all satisfy some preficate. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @param from the index where the search starts. + * @return the length of the longest segment of this $coll starting from index `from` + * such that every element of the segment satisfies the predicate `p`. */ def segmentLength(p: A => Boolean, from: Int): Int = { var i = 0 @@ -163,26 +228,34 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => i } - /** Returns length of longest prefix of this seqence - * such that every element of the prefix satisfies predicate `p`. - * @note may not terminate for infinite-sized collections. - * @param p the predicate + /** Returns the length of the longest prefix whose elements all satisfy some preficate. + * + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the length of the longest prefix of this $coll + * such that every element of the segment satisfies the predicate `p`. */ def prefixLength(p: A => Boolean) = segmentLength(p, 0) - /** Returns index of the first element satisfying a predicate, or -1, if none exists. + /** Finds index of first element satisfying some predicate. * - * @note may not terminate for infinite-sized collections. - * @param p the predicate + * $mayNotTerminateInf + * + * @param p the predicate used to test elements. + * @return the index of the first element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. */ def indexWhere(p: A => Boolean): Int = indexWhere(p, 0) - /** Returns index of the first element starting from a start index - * satisying a predicate, or -1, if none exists. + /** Finds index of the first element satisfying some predicate after or at some start index. + * + * $mayNotTerminateInf * - * @note may not terminate for infinite-sized collections. - * @param p the predicate - * @param from the start index + * @param p the predicate used to test elements. + * @param from the start index + * @return the index `>= from` of the first element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. */ def indexWhere(p: A => Boolean, from: Int): Int = { var i = from @@ -192,63 +265,95 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => if (it.hasNext) i else -1 } - /** Returns index of the first element satisying a predicate, or -1. */ - @deprecated("Use `indexWhere' instead") + /** Returns index of the first element satisying a predicate, or `-1`. + * @deprecated "Use `indexWhere` instead" + */ def findIndexOf(p: A => Boolean): Int = indexWhere(p) - /** Returns the index of the first occurence of the specified - * object in this sequence. + /** Finds index of first occurrence of some value in this $coll. * - * @note may not terminate for infinite-sized collections. - * @param elem element to search for. - * @return the index in this sequence of the first occurence of the - * specified element, or -1 if the sequence does not contain - * this element. + * $mayNotTerminateInf + * + * @param elem the element value to search for. + * @tparam B the type of the element `elem`. + * @return the index of the first element of this $coll that is equal (wrt `==`) + * tp `elem`, or `-1`, if none exists. + * + * @usecase def indexOf(elem: A): Int + * + * @param elem the element value to search for. + * @return the index of the first element of this $coll that is equal (wrt `==`) + * to `elem`, or `-1`, if none exists. */ def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) - /** Returns the index of the first occurence of the specified - * object in this sequence, starting from a start index, or - * -1, if none exists. + /** Finds index of first occurrence of some value in this $coll after or at some start index. + * + * $mayNotTerminateInf * - * @note may not terminate for infinite-sized collections. - * @param elem element to search for. + * @param elem the element value to search for. + * @tparam B the type of the element `elem`. + * @param from the start index + * @return the index `>= from` of the first element of this $coll that is equal (wrt `==`) + * tp `elem`, or `-1`, if none exists. + * + * @usecase def indexOf(elem: A): Int + * + * @param elem the element value to search for. + * @param from the start index + * @return the index `>= from` of the first element of this $coll that is equal (wrt `==`) + * to `elem`, or `-1`, if none exists. */ def indexOf[B >: A](elem: B, from: Int): Int = indexWhere(elem ==, from) - /** Returns the index of the last occurence of the specified element - * in this sequence, or -1 if the sequence does not contain this element. + /** Finds index of last occurrence of some value in this $coll. + * + * $willNotTerminateInf + * + * @param elem the element value to search for. + * @tparam B the type of the element `elem`. + * @return the index of the last element of this $coll that is equal (wrt `==`) + * to `elem`, or `-1`, if none exists. * - * @param elem element to search for. - * @return the index in this sequence of the last occurence of the - * specified element, or -1 if the sequence does not contain - * this element. + * @usecase def lastIndexOf(elem: A): Int + * + * @param elem the element value to search for. + * @return the index of the last element of this $coll that is equal (wrt `==`) + * to `elem`, or `-1`, if none exists. */ def lastIndexOf[B >: A](elem: B): Int = lastIndexWhere(elem ==) - /** Returns the index of the last - * occurence of the specified element in this sequence - * before or at a given end index, - * or -1 if the sequence does not contain this element. - * - * @param elem element to search for. - * @param end the end index - */ + /** Finds index of last occurrence of some value in this $coll before or at a given end index. + * + * @param elem the element value to search for. + * @param end the end index. + * @tparam B the type of the element `elem`. + * @return the index `<= end` of the last element of this $coll that is equal (wrt `==`) + * to `elem`, or `-1`, if none exists. + * + * @usecase def lastIndexOf(elem: A): Int + * + * @param elem the element value to search for. + * @return the index `<= end` of the last element of this $coll that is equal (wrt `==`) + * to `elem`, or `-1`, if none exists. + */ def lastIndexOf[B >: A](elem: B, end: Int): Int = lastIndexWhere(elem ==, end) - /** Returns index of the last element satisying a predicate, or -1, if none exists. + /** Finds index of last element satisfying some predicate. + * + * $willNotTerminateInf * - * @param p the predicate - * @return the index of the last element satisfying <code>p</code>, - * or -1 if such an element does not exist + * @param p the predicate used to test elements. + * @return the index of the last element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. */ def lastIndexWhere(p: A => Boolean): Int = lastIndexWhere(p, length - 1) - /** Returns index of the last element not exceeding a given end index - * and satisying a predicate, or -1 if none exists. + /** Finds index of last element satisfying some predicate before or at given end index. * - * @param end the end index - * @param p the predicate + * @param p the predicate used to test elements. + * @return the index `<= end` of the last element of this $coll that satisfies the predicate `p`, + * or `-1`, if none exists. */ def lastIndexWhere(p: A => Boolean, end: Int): Int = { var i = length - 1 @@ -257,8 +362,11 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => i } - /** A sequence of type <code>C</code> consisting of all elements of - * this sequence in reverse order. + /** Returns new $coll wih elements in reversed order. + * + * $willNotTerminateInf + * + * @return A new $coll with all elements of this $coll in reversed order. */ def reverse: Repr = { var xs: List[A] = List() @@ -270,18 +378,34 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => b.result } - /** Apply a function to all the elements of the sequence, and return the - * reversed sequence of results. This is equivalent to a call to <code>reverse</code> - * followed by a call to <code>map</code>, but more efficient. + /** + * Builds a new collection by applying a function to all elements of this $coll and + * collecting the results in reversed order. + * + * $willNotTerminateInf + * + * Note: `xs.reverseMap(f)` is the same as `xs.reverse.map(f)` but might be more efficient. + * + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @tparam That $thatinfo + * @param bf $bfinfo + * @return a new collection of type `That` resulting from applying the given function + * `f` to each element of this $coll and collecting the results in reversed order. + * + * @usecase def reverseMap[B](f: A => B): $Coll[B] + * + * Note: `xs.reverseMap(f)` is the same as `xs.reverse.map(f)` but might be more efficient. * - * @param f the function to apply to each elements. - * @return the reversed seq of results. + * @param f the function to apply to each element. + * @tparam B the element type of the returned collection. + * @return a new $coll resulting from applying the given function + * `f` to each element of this $coll and collecting the results in reversed order. */ def reverseMap[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { var xs: List[A] = List() for (x <- this) xs = x :: xs - val b = bf(repr) for (x <- xs) b += f(x) @@ -289,25 +413,30 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => b.result } - /** The elements of this sequence in reversed order + /** An iterator yielding elements in reversed order. + * + * $willNotTerminateInf + * + * Note: `xs.reverseIterator` is the same as `xs.reverse.iterator` but might be more efficient. + * + * @return an iterator yielding the elements of this $coll in reversed order */ def reverseIterator: Iterator[A] = toCollection(reverse).iterator + /** @deprecated use `reverseIterator` instead */ @deprecated("use `reverseIterator' instead") def reversedElements = reverseIterator - /** - * Checks whether the argument sequence is contained at the - * specified index within the receiver object. + /** Checks whether this $coll contains the given sequence at a given index. * * If the both the receiver object, <code>this</code> and * the argument, <code>that</code> are infinite sequences * this method may not terminate. * - * @return true if <code>that</code> is contained in - * <code>this</code>, at the specified index, otherwise false - * - * @see String.startsWith + * @param that the candidate sequence + * @param offset the index where the sequence is searched. + * @return `true` if the sequence `that` is contained in this $coll at index `offset`, + * otherwise `false`. */ def startsWith[B](that: Seq[B], offset: Int): Boolean = { val i = this.iterator drop offset @@ -319,10 +448,10 @@ trait SeqLike[+A, +Repr] extends IterableLike[A, Repr] { self => !j.hasNext } - /** - * Check whether the receiver object starts with the argument sequence. + /** Checks whether this $coll starts with the given sequence. * - * @return true if <code>that</code> is a prefix of <code>this</code>, + * @param that the candidate sequence + * @return `true` if this collection has `that` as a prefix, `false` otherwise. * otherwise false */ def startsWith[B](that: Seq[B]): Boolean = startsWith(that, 0) diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala index 44cb7ced63..0ff6227a3a 100644 --- a/src/library/scala/collection/TraversableLike.scala +++ b/src/library/scala/collection/TraversableLike.scala @@ -63,14 +63,12 @@ import immutable.{List, Stream, Nil, ::} * * @define Coll Traversable * @define coll traversable collection - * @define thatinfo - * the class of the returned collection. Where possible, `That` is + * @define thatinfo the class of the returned collection. Where possible, `That` is * the same class as the current collection class `Repr`, but this * depends on the element type `B` being admissible for that class, * which means that an implicit instance of type `CanBuildFrom[Repr, B, That]` * is found. - * @define bfinfo - * an implicit value of class `CanBuildFrom` which determines the + * @define bfinfo an implicit value of class `CanBuildFrom` which determines the * result class `That` from the current representation type `Repr` * and the new element type `B`. * @define orderDependent @@ -100,12 +98,13 @@ self => */ def repr: Repr = this.asInstanceOf[Repr] - /** The underlying collection seen as an instance of `Traversable`. - * By default this is implemented as the `TraversableLike` object itself, but this can be overridden. + /** The underlying collection seen as an instance of `$Coll`. + * By default this is implemented as the current collection object itself, + * but this can be overridden. */ protected[this] def thisCollection: Traversable[A] = this.asInstanceOf[Traversable[A]] - /** A conversion from collections of type `Repr` to `Traversable` objects. + /** A conversion from collections of type `Repr` to `$Coll` objects. * By default this is implemented as just a cast, but this can be overridden. */ protected[this] def toCollection(repr: Repr): Traversable[A] = repr.asInstanceOf[Traversable[A]] @@ -116,8 +115,9 @@ self => /** Applies a function `f` to all elements of this $coll. * - * Note: this method underlies the implementation of most other bulk operations. - * It's important to implement this method in an efficient way. + * Note: this method underlies the implementation of most other bulk operations. + * It's important to implement this method in an efficient way. + * * * @param f the function that is applied for its side-effect to every element. * The result of function `f` is discarded. @@ -126,7 +126,7 @@ self => * This result will always be ignored. Typically `U` is `Unit`, * but this is not necessary. * - * @usage def foreach(f: A => Unit): Unit + * @usecase def foreach(f: A => Unit): Unit * * @param f the function that is applied for its side-effect to every element. * The result of function `f` is discarded. @@ -184,7 +184,7 @@ self => * @return a new collection of type `That` which contains all elements of this $coll * followed by all elements of `that`. * - * @usage def ++(that: Traversable[A]): $Coll[A] + * @usecase def ++(that: Traversable[A]): $Coll[A] * * @param that the traversable to append. * @return a new $coll which contains all elements of this $coll @@ -206,7 +206,7 @@ self => * @return a new collection of type `That` which contains all elements of this $coll * followed by all elements of `that`. * - * @usage def ++(that: Iterator[A]): $Coll[A] + * @usecase def ++(that: Iterator[A]): $Coll[A] * * @param that the iterator to append. * @return a new $coll which contains all elements of this $coll @@ -228,7 +228,7 @@ self => * @return a new collection of type `That` resulting from applying the given function * `f` to each element of this $coll and collecting the results. * - * @usage def map[B](f: A => B): $Coll[B] + * @usecase def map[B](f: A => B): $Coll[B] * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. @@ -251,7 +251,7 @@ self => * @return a new collection of type `That` resulting from applying the given collection-valued function * `f` to each element of this $coll and concatenating the results. * - * @usage def flatMap[B](f: A => Traversable[B]): $Coll[B] + * @usecase def flatMap[B](f: A => Traversable[B]): $Coll[B] * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. @@ -266,7 +266,7 @@ self => /** Selects all elements of this $coll which satisfy a predicate. * - * @param p the predicate used used to test elements. + * @param p the predicate used to test elements. * @return a new $coll consisting of all elements of this $coll that satisfy the given * predicate `p`. The order of the elements is preserved. */ @@ -296,7 +296,7 @@ self => * `pf` to each element on which it is defined and collecting the results. * The order of the elements is preserved. * - * @usage def partialMap[B](pf: PartialFunction[Any, B]): $Coll[B] + * @usecase def partialMap[B](pf: PartialFunction[Any, B]): $Coll[B] * * @param pf the partial function which filters and maps the $coll. * @return a new $coll resulting from applying the given partial function @@ -309,6 +309,34 @@ self => b.result } + /** Builds a new collection by applying an option-valued function to all elements of this $coll + * on which the function is defined. + * + * @param f the option-valued function which filters and maps the $coll. + * @tparam B the element type of the returned collection. + * @tparam That $thatinfo + * @param bf $bfinfo + * @return a new collection of type `That` resulting from applying the option-valued function + * `f` to each element and collecting all defined results. + * The order of the elements is preserved. + * + * @usecase def filterMap[B](f: A => Option[B]): $Coll[B] + * + * @param pf the partial function which filters and maps the $coll. + * @return a new $coll resulting from applying the given option-valued function + * `f` to each element and collecting all defined results. + * The order of the elements is preserved. + def filterMap[B, That](f: A => Option[B])(implicit bf: CanBuildFrom[Repr, B, That]): That = { + val b = bf(repr) + for (x <- this) + f(x) match { + case Some(y) => b += y + case _ => + } + b.result + } + */ + /** Partitions this $coll in two ${coll}s according to a predicate. * * @param p the predicate on which to partition. @@ -356,7 +384,7 @@ self => * * $mayNotTerminateInf * - * @param p the predicate used used to test elements. + * @param p the predicate used to test elements. * @return `true` if the given predicate `p` holds for all elements * of this $coll, otherwise `false`. */ @@ -373,7 +401,7 @@ self => * * $mayNotTerminateInf * - * @param p the predicate used used to test elements. + * @param p the predicate used to test elements. * @return `true` if the given predicate `p` holds for some of the elements * of this $coll, otherwise `false`. */ @@ -388,7 +416,7 @@ self => /** Counts the number of elements in the $coll which satisfy a predicate. * - * @param p the predicate used used to test elements. + * @param p the predicate used to test elements. * @return the number of elements satisfying the predicate `p`. * * @@ -406,7 +434,7 @@ self => * $mayNotTerminateInf * $orderDependent * - * @param p the predicate used used to test elements. + * @param p the predicate used to test elements. * @return an option value containing the first element in the $coll * that satisfies `p`, or `None` if none exists. */ @@ -419,6 +447,28 @@ self => result } + /** Applies option-valued function to successive elements of this $coll + * until a defined value is found. + * + * $mayNotTerminateInf + * $orderDependent + * + * @param f the function to be applied to successive elements. + * @return an option value containing the first defined result of + * `f`, or `None` if `f` returns `None` for all all elements. + def mapFind[B](f: A => Option[B]): Option[B] = { + var result: Option[B] = None + breakable { + for (x <- this) + f(x) match { + case s @ Some(_) => result = s; break + case _ => + } + } + result + } + */ + /** Applies a binary operator to a start value and all elements of this $coll, going left to right. * * $willNotTerminateInf @@ -430,9 +480,9 @@ self => * @return the result of inserting `op` between consecutive elements of this $coll$, * going left to right with the start value `z` on the left: * {{{ - * op(...op(z, x_1), x_2, ..., x_n) + * op(...op(z, x,,1,,), x,,2,,, ..., x,,n,,) * }}} - * where `x_1, ..., x_n` are the elements of this $coll. + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. * */ def foldLeft[B](z: B)(op: (B, A) => B): B = { @@ -454,9 +504,9 @@ self => * @return the result of inserting `op` between consecutive elements of this $coll$, * going left to right with the start value `z` on the left: * {{{ - * op(...op(op(z, x_1), x_2), ..., x_n) + * op(...op(op(z, x,,1,,), x,,2,,), ..., x,,n,,) * }}} - * where `x_1, ..., x_n` are the elements of this $coll. + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. */ def /: [B](z: B)(op: (B, A) => B): B = foldLeft(z)(op) @@ -467,9 +517,9 @@ self => * @return the result of inserting `op` between consecutive elements of this $coll$, * going right to left with the start value `z` on the right: * {{{ - * op(x_1, op(x_2, ... op(x_n, z)...)) + * op(x,,1,,, op(x,,2,,, ... op(x,,n,,, z)...)) * }}} - * where `x_1, ..., x_n` are the elements of this $coll. + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. * * $willNotTerminateInf * $orderDependentFold @@ -492,9 +542,9 @@ self => * @return the result of inserting `op` between consecutive elements of this $coll$, * going right to left with the start value `z` on the right: * {{{ - * op(x_1, op(x_2, ... op(x_n, z)...)) + * op(x,,1,,, op(x,,2,,, ... op(x,,n,,, z)...)) * }}} - * where `x_1, ..., x_n` are the elements of this $coll. + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. */ def :\ [B](z: B)(op: (A, B) => B): B = foldRight(z)(op) @@ -507,9 +557,9 @@ self => * @return the result of inserting `op` between consecutive elements of this $coll$, * going left to right: * {{{ - * op(...(op(x_1, x_2), ... ) , x_n) + * op(...(op(x,,1,,, x,,2,,), ... ) , x,,n,,) * }}} - * where `x_1, ..., x_n` are the elements of this $coll. + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. * @throws `UnsupportedOperationException` if this $coll is empty. */ def reduceLeft[B >: A](op: (B, A) => B): B = { @@ -544,9 +594,9 @@ self => * @return the result of inserting `op` between consecutive elements of this $coll$, * going right to left: * {{{ - * op(x_1, op(x_2, ..., op(x_(n-1), x_n)...)) + * op(x,,1,,, op(x,,2,,, ..., op(x,,n-1,,, x,,n,,)...)) * }}} - * where `x_1, ..., x_n` are the elements of this $coll. + * where `x,,1,,, ..., x,,n,,` are the elements of this $coll. * @throws `UnsupportedOperationException` if this $coll is empty. */ def reduceRight[B >: A](op: (A, B) => B): B = { @@ -576,9 +626,13 @@ self => * @tparam B the result type of the `+` operator. * @return the sum of all elements of this $coll with respect to the `+` operator in `num`. * - * @usage sum: Num - * @return the sum of all elements in this $coll of numbers of type `Num`. - * `Num` must be a numeric type such as `Int`, `Double`, `BigDecimal`. + * @usecase def sum: Int + * + * @return the sum of all elements in this $coll of numbers of type `Int`. + * Instead of `Int`, any other type `T` with an implicit `Numeric[T]` implementation + * can be used as element type of the $coll and as result type of `sum`. + * Examples of such types are: `Long`, `Float`, `Double`, `BigInt`. + * */ def sum[B >: A](implicit num: Numeric[B]): B = { var acc = num.zero @@ -593,9 +647,12 @@ self => * @tparam B the result type of the `*` operator. * @return the product of all elements of this $coll with respect to the `*` operator in `num`. * - * @usage product: Num - * @return the product of all elements in this $coll of numbers of type `Num`. - * `Num` must be a numeric type such as `Int`, `Double`, `BigDecimal`. + * @usecase def product: Int + * + * @return the product of all elements in this $coll of numbers of type `Int`. + * Instead of `Int`, any other type `T` with an implicit `Numeric[T]` implementation + * can be used as element type of the $coll and as result type of `product`. + * Examples of such types are: `Long`, `Float`, `Double`, `BigInt`. */ def product[B >: A](implicit num: Numeric[B]): B = { var acc = num.one @@ -609,7 +666,7 @@ self => * @tparam B The type over which the ordering is defined. * @return the smallest element of this $coll with respect to the ordering `cmp`. * - * @usage min: A + * @usecase def min: A * @return the smallest element of this $coll */ def min[B >: A](implicit cmp: Ordering[B]): A = { @@ -626,7 +683,7 @@ self => * @tparam B The type over which the ordering is defined. * @return the largest element of this $coll with respect to the ordering `cmp`. * - * @usage min: A + * @usecase def min: A * @return the largest element of this $coll. */ def max[B >: A](implicit cmp: Ordering[B]): A = { @@ -861,7 +918,7 @@ self => * @tparam B the type of the elements of the array. * * - * @usage copyToArray(xs: Array[A], start: Int, len: Int): Unit + * @usecase def copyToArray(xs: Array[A], start: Int, len: Int): Unit * * @param xs the array to fill. * @param start the starting index. @@ -891,7 +948,7 @@ self => * @param start the starting index. * @tparam B the type of the elements of the array. * - * @usage def copyToArray(xs: Array[A], start: Int): Unit + * @usecase def copyToArray(xs: Array[A], start: Int): Unit * * @param xs the array to fill. * @param start the starting index. @@ -907,7 +964,7 @@ self => * be available. * @return an array containing all elements of this $coll. * - * @usage toArray: Array[A] + * @usecase def toArray: Array[A] * @return an array containing all elements of this $coll. * A `ClassManifest` must be available for the element type of this $coll. */ @@ -1054,7 +1111,6 @@ self => /** Creates a non-strict view of this $coll. * * @return a non-strict view of this $coll. - * @see TraversableView */ def view = new TraversableView[A, Repr] { protected lazy val underlying = self.repr @@ -1107,7 +1163,7 @@ self => * `f` to each element of the outer $coll that satisfies predicate `p` * and collecting the results. * - * @usage def map[B](f: A => B): $Coll[B] + * @usecase def map[B](f: A => B): $Coll[B] * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. @@ -1132,7 +1188,7 @@ self => * @return a new collection of type `That` resulting from applying the given collection-valued function * `f` to each element of the outer $coll that satisfies predicate `p` and concatenating the results. * - * @usage def flatMap[B](f: A => Traversable[B]): $Coll[B] + * @usecase def flatMap[B](f: A => Traversable[B]): $Coll[B] * * @param f the function to apply to each element. * @tparam B the element type of the returned collection. @@ -1156,7 +1212,7 @@ self => * This result will always be ignored. Typically `U` is `Unit`, * but this is not necessary. * - * @usage def foreach(f: A => Unit): Unit + * @usecase def foreach(f: A => Unit): Unit * * @param f the function that is applied for its side-effect to every element. * The result of function `f` is discarded. diff --git a/src/library/scala/collection/mutable/MapLike.scala b/src/library/scala/collection/mutable/MapLike.scala index d03858a68f..92f1b5bba6 100644 --- a/src/library/scala/collection/mutable/MapLike.scala +++ b/src/library/scala/collection/mutable/MapLike.scala @@ -103,6 +103,15 @@ trait MapLike[A, B, +This <: MapLike[A, B, This] with Map[A, B]] */ override def updated[B1 >: B](key: A, value: B1): mutable.Map[A, B1] = this + ((key, value)) + /** If given key is already in this map, returns associated value + * Otherwise, computes value from given expression `op`, stores with key + * in map and returns that value. + */ + def cached(key: A, op: => B) = get(key) match { + case Some(v) => v + case None => val v = op; update(key, v); v + } + /** Add a new key/value mapping and return the map itself. * * @param kv the key/value mapping to be added |