From 1e88594f358d08b5e9b22ba87280003a581359e4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 7 Dec 2009 17:31:49 +0000 Subject: new doc comment generation, including some new ... new doc comment generation, including some new style doc comments in collection classes. --- src/compiler/scala/tools/nsc/Global.scala | 35 +- src/compiler/scala/tools/nsc/ast/DocComments.scala | 365 +++++++++++++++++++++ .../scala/tools/nsc/ast/TreePrinters.scala | 2 +- src/compiler/scala/tools/nsc/ast/Trees.scala | 8 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 6 +- .../scala/tools/nsc/ast/parser/Scanners.scala | 10 +- src/compiler/scala/tools/nsc/doc/DocFactory.scala | 2 +- .../scala/tools/nsc/doc/SourcelessComments.scala | 94 +++--- .../scala/tools/nsc/interactive/Global.scala | 5 +- .../scala/tools/nsc/reporters/Reporter.scala | 9 + .../scala/tools/nsc/symtab/SymbolTable.scala | 3 +- .../tools/nsc/symtab/classfile/UnPickler.scala | 2 +- .../scala/tools/nsc/typechecker/Namers.scala | 3 - .../tools/nsc/typechecker/SuperAccessors.scala | 11 + .../scala/tools/nsc/typechecker/Typers.scala | 63 +++- src/compiler/scala/tools/nsc/util/Position.scala | 5 + src/library/scala/collection/IndexedSeqLike.scala | 13 +- src/library/scala/collection/IterableLike.scala | 333 +++++++++---------- src/library/scala/collection/Iterator.scala | 15 + src/library/scala/collection/LinearSeqLike.scala | 12 +- src/library/scala/collection/MapLike.scala | 40 +-- src/library/scala/collection/SeqLike.scala | 325 ++++++++++++------ src/library/scala/collection/TraversableLike.scala | 150 ++++++--- src/library/scala/collection/mutable/MapLike.scala | 9 + 24 files changed, 1074 insertions(+), 446 deletions(-) create mode 100755 src/compiler/scala/tools/nsc/ast/DocComments.scala (limited to 'src') 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

$2"""), + ("""\{\{\{(.*(?:\n.*)*)\}\}\}""".r, """

$1
"""), + ("""`([^`]*)`""" .r, """$1"""), + ("""__([^_]*)__""" .r, """$1"""), + ("""''([^']*)''""" .r, """$1"""), + ("""'''([^']*)'''""" .r, """$1"""), + ("""\^([^^]*)\^""" .r, """$1"""), + (""",,([^,]*),,""" .r, """$1""")) + 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 "

" + if (firstParBreak == -1) firstParBreak = mainComment.length - 2 + val comment = mainComment.substring(0, firstParBreak)+"

"+ + 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(""" /**

* Class Nothing is - together with class * Null - at the bottom of the @@ -44,9 +44,9 @@ abstract class SourcelessComments { * instance of List[T], for * any element type T. *

*/ - """ + """) - comment(NullClass) = """ + comment(NullClass) = new DocComment(""" /**

* Class Null is - together with class * Nothing - at the bottom of the @@ -61,12 +61,12 @@ abstract class SourcelessComments { * it is not possible to assign null to a variable of * type Int. *

*/ - """ + """) /*******************************************************************/ /* Documentation for Any */ - comment(AnyClass) = """ + comment(AnyClass) = new DocComment(""" /**

* Class Any is the root of the AnyRef and * AnyVal. *

*/ - """ + """) - comment(Any_equals) = """ + comment(Any_equals) = new DocComment(""" /** This method is used to compare the receiver object (this) * with the argument object (arg0) for equivalence. * @@ -113,34 +113,34 @@ abstract class SourcelessComments { * @param arg0 the object to compare against this object for equality. * @return true if the receiver object is equivalent to the argument; false otherwise. *

*/ - """ + """) - comment(Any_==) = """ + comment(Any_==) = new DocComment(""" /** `o == arg0` is the same as `o.equals(arg0)`. *

* @param arg0 the object to compare against this object for equality. * @return `true` if the receiver object is equivalent to the argument; `false` otherwise. *

*/ - """ + """) - comment(Any_!=) = """ + comment(Any_!=) = new DocComment(""" /** `o != arg0` is the same as `!(o == (arg0))`. *

* @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. *

*/ - """ + """) - comment(Any_toString) = """ + comment(Any_toString) = new DocComment(""" /** Returns a string representation of the object. *

* The default representation is platform dependent. * * @return a string representation of the object. *

*/ - """ + """) - comment(Any_asInstanceOf) = """ + comment(Any_asInstanceOf) = new DocComment(""" /**This method is used to cast the receiver object to be of type T0. * *

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 T0. * @return the receiver object. *

*/ - """ + """) - comment(Any_isInstanceOf) = """ + comment(Any_isInstanceOf) = new DocComment(""" /** This method is used to test whether the dynamic type of the receiver object is T0. * *

Note that the test result of the test is modulo Scala's erasure @@ -172,9 +172,9 @@ abstract class SourcelessComments { * * @return true if the receiver object is an * instance of erasure of type T0; false otherwise. */ - """ + """) - comment(Any_hashCode) = """ + comment(Any_hashCode) = new DocComment(""" /** Returns a hash code value for the object. * *

@@ -195,36 +195,36 @@ abstract class SourcelessComments { *

* @return the hash code value for the object. *

*/ - """ + """) /*******************************************************************/ /* Documentation for AnyRef */ - comment(AnyRefClass) = """ + comment(AnyRefClass) = new DocComment(""" /**

* Class AnyRef is the root class of all * reference types. *

*/ - """ + """) - comment(Object_==) = """ + comment(Object_==) = new DocComment(""" /** o == arg0 is the same as if (o eq null) arg0 eq null else o.equals(arg0). *

* @param arg0 the object to compare against this object for equality. * @return true if the receiver object is equivalent to the argument; false otherwise. *

*/ - """ + """) - comment(Object_ne) = """ + comment(Object_ne) = new DocComment(""" /** o.ne(arg0) is the same as !(o.eq(arg0)). *

* @param arg0 the object to compare against this object for reference dis-equality. * @return false if the argument is not a reference to the receiver object; true otherwise. *

*/ - """ + """) - 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. *

@@ -232,9 +232,9 @@ abstract class SourcelessComments { * invoked, as well as the interaction between finalize * and non-local returns and exceptions, are all platform dependent. *

*/ - """ + """) - comment(Object_clone) = """ + comment(Object_clone) = new DocComment(""" /** This method creates and returns a copy of the receiver object. * *

@@ -242,9 +242,9 @@ abstract class SourcelessComments { * * @return a copy of the receiver object. *

*/ - """ + """) - comment(Object_getClass) = """ + comment(Object_getClass) = new DocComment(""" /** Returns a representation that corresponds to the dynamic class of the receiver object. * *

@@ -252,17 +252,17 @@ abstract class SourcelessComments { * * @return a representation that corresponds to the dynamic class of the receiver object. *

*/ - """ + """) - 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 (arg0) is a reference to the * receiver object (this). * @@ -304,11 +304,11 @@ abstract class SourcelessComments { * @param arg0 the object to compare against this object for reference equality. * @return true if the argument is a reference to the receiver object; false otherwise. *

*/ - """ + """) /*******************************************************************/ - comment(AnyValClass) = """ + comment(AnyValClass) = new DocComment(""" /**

* Class AnyVal is the root class of all * value types. @@ -338,25 +338,25 @@ abstract class SourcelessComments { * Double are called * floating point types. *

*/ - """ + """) - comment(BooleanClass) = """ + comment(BooleanClass) = new DocComment(""" /**

* Class Boolean has only two values: true * and false. *

*/ - """ + """) - comment(UnitClass) = """ + comment(UnitClass) = new DocComment(""" /**

* Class Unit has only one value: (). *

*/ - """ + """) 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(""" /**

* Class """ + sym.name + """ belongs to the value * classes whose instances are not represented as objects by the @@ -370,7 +370,7 @@ abstract class SourcelessComments { * Values """ + maxValue + """ and """ + minValue + """ * are in defined in object scala.Math. *

*/ - """ + """) } 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 /**

- * A template trait for iterable collections. + * A template trait for iterable collections of type `Iterable[A]`. *

*

* Collection classes mixing in this trait provide a method @@ -36,11 +36,27 @@ import annotation.unchecked.uncheckedVariance * Iterable. *

* - * @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 f 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 p, or None 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 f, from right to left, and starting with - * the value z. + * 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 f(a0, f(a1, f(..., f(an, z)...))) - * if the iterable is [a0, a1, ..., an]. - */ - 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 op, 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 a0 op (... op (an-1 op an)...) - * if the iterable object has elements a0, a1, ..., - * an. + * @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 n - * elements of this iterable, or else the whole iterable, if it has less - * than n 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 p. - * - * @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 n 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 n 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 xs 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 - * that 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 that may have a different length - * as the self iterable. - * @param thisElem element thisElem is used to fill up the - * resulting iterable if the self iterable is shorter than - * that - * @param thatElem element thatElem is used to fill up the - * resulting iterable if that is shorter than - * the self iterable - * @return Sequence((a0,b0), ..., - * (an,bn), (elem,bn+1), - * ..., {elem,bm}) - * when [a0, ..., an] zip - * [b0, ..., bm] is - * invoked where m > n. + * $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 - /** None if iterable is empty. */ + /** None 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 filter, * map, and flatMap 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 f, from left to right, and starting with * the value z. 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._ -/**

- * 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 +/** + * 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): - *

- *
- *    def get(key: A): Option[B]
- *    def iterator: Iterator[(A, B)]
- *    def + [B1 >: B](kv: (A, B1)): This
- *    def -(key: A): This
- *

+ * {{{ + * def get(key: A): Option[B] + * + * def iterator: Iterator[(A, B)] + * + * def + [B1 >: B](kv: (A, B1)): This + * + * def -(key: A): This + * }}} * If you wish that methods like, take, drop, * filter return the same kind of map, you should also override: - *

- *
- *   def empty: This
- *

- * It is also good idea to override methods foreach and - * size for efficiency. - *

+ * {{{ + * 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 Seq[A] represents sequences of elements - * of type A. - * 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 A. + * + * 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 length with operand len. - * returns x where - * x < 0 iff this.length < len - * x == 0 iff this.length == len - * x > 0 iff this.length > len. + /** 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 length */ + /** 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 x? + /** 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 p, - * 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 C 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 reverse - * followed by a call to map, 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, this and * the argument, that are infinite sequences * this method may not terminate. * - * @return true if that is contained in - * this, 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 that is a prefix of this, + * @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 -- cgit v1.2.3