diff options
author | Lukas Rytz <lukas.rytz@typesafe.com> | 2015-09-22 19:44:09 +0200 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@typesafe.com> | 2015-09-22 19:44:09 +0200 |
commit | df704ef7389ff6265afc1278783489ee97ee4ce7 (patch) | |
tree | ca7e4df33c4e5404f18e0b664e28c7c8bc4e3375 /src | |
parent | 9995935b6160171527b121263db75b56be6a9ca7 (diff) | |
parent | fe755d5ff2518ff94d6a0dee43f14ba24f9af5eb (diff) | |
download | scala-df704ef7389ff6265afc1278783489ee97ee4ce7.tar.gz scala-df704ef7389ff6265afc1278783489ee97ee4ce7.tar.bz2 scala-df704ef7389ff6265afc1278783489ee97ee4ce7.zip |
Merge pull request #4761 from lrytz/merge-2.11-to-2.12-sep-22
Merge 2.11 to 2.12 sep 22
Diffstat (limited to 'src')
77 files changed, 888 insertions, 298 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/DocComments.scala b/src/compiler/scala/tools/nsc/ast/DocComments.scala index c70690e697..c70690e697 100755..100644 --- a/src/compiler/scala/tools/nsc/ast/DocComments.scala +++ b/src/compiler/scala/tools/nsc/ast/DocComments.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala index 52b8a51a79..52b8a51a79 100755..100644 --- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala index 67241ef639..67241ef639 100755..100644 --- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala b/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala index 6dcfa173df..6dcfa173df 100755..100644 --- a/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/xml/Utility.scala diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 74d152a4cf..70170d9e97 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -172,6 +172,7 @@ trait ScalaSettings extends AbsScalaSettings val YconstOptimization = BooleanSetting ("-Yconst-opt", "Perform optimization with constant values.") val Ycompacttrees = BooleanSetting ("-Ycompact-trees", "Use compact tree printer when displaying trees.") val noCompletion = BooleanSetting ("-Yno-completion", "Disable tab-completion in the REPL.") + val completion = ChoiceSetting ("-Ycompletion", "provider", "Select tab-completion in the REPL.", List("pc","adhoc","none"), "pc") val Xdce = BooleanSetting ("-Ydead-code", "Perform dead code elimination.") val debug = BooleanSetting ("-Ydebug", "Increase the quantity of debugging output.") //val doc = BooleanSetting ("-Ydoc", "Generate documentation") @@ -364,8 +365,8 @@ trait ScalaSettings extends AbsScalaSettings */ val YpresentationVerbose = BooleanSetting("-Ypresentation-verbose", "Print information about presentation compiler tasks.") val YpresentationDebug = BooleanSetting("-Ypresentation-debug", "Enable debugging output for the presentation compiler.") - val YpresentationStrict = BooleanSetting("-Ypresentation-strict", "Do not report type errors in sources with syntax errors.") - + val YpresentationAnyThread = BooleanSetting("-Ypresentation-any-thread", "Allow use of the presentation compiler from any thread") + val YpresentationStrict = BooleanSetting("-Ypresentation-strict", "Do not report type errors in sources with syntax errors.") val YpresentationLog = StringSetting("-Ypresentation-log", "file", "Log presentation compiler events into file", "") val YpresentationReplay = StringSetting("-Ypresentation-replay", "file", "Replay presentation compiler events from file", "") val YpresentationDelay = IntSetting("-Ypresentation-delay", "Wait number of ms after typing before starting typechecking", 0, Some((0, 999)), str => Some(str.toInt)) diff --git a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala index 49a4990722..62d9c497ba 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/Logic.scala @@ -38,7 +38,7 @@ trait Logic extends Debugging { padded.transpose.map(alignedColumns).transpose map (_.mkString(sep)) mkString(lineSep) } - // http://www.cis.upenn.edu/~cis510/tcl/chap3.pdf + // ftp://ftp.cis.upenn.edu/pub/cis511/public_html/Spring04/chap3.pdf // http://users.encs.concordia.ca/~ta_ahmed/ms_thesis.pdf // propositional logic with constants and equality trait PropositionalLogic { diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala index 00de77a8d4..f259283e48 100644 --- a/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala +++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchAnalysis.scala @@ -150,7 +150,11 @@ trait TreeAndTypeAnalysis extends Debugging { acc: List[List[Type]]): List[List[Type]] = wl match { case hd :: tl => val children = enumerateChildren(hd) - groupChildren(tl ++ children, acc :+ filterChildren(children)) + // put each trait in a new group, since traits could belong to the same + // group as a derived class + val (traits, nonTraits) = children.partition(_.isTrait) + val filtered = (traits.map(List(_)) ++ List(nonTraits)).map(filterChildren) + groupChildren(tl ++ children, acc ++ filtered) case Nil => acc } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index c28cd8d69c..3fe2f24818 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -574,19 +574,23 @@ trait Contexts { self: Analyzer => /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ private[typechecker] def issueAmbiguousError(err: AbsAmbiguousTypeError) = reporter.issueAmbiguousError(err)(this) /** Issue/throw the given error message according to the current mode for error reporting. */ - def error(pos: Position, msg: String) = reporter.error(pos, msg) + def error(pos: Position, msg: String) = reporter.error(fixPosition(pos), msg) /** Issue/throw the given error message according to the current mode for error reporting. */ - def warning(pos: Position, msg: String) = reporter.warning(pos, msg) - def echo(pos: Position, msg: String) = reporter.echo(pos, msg) + def warning(pos: Position, msg: String) = reporter.warning(fixPosition(pos), msg) + def echo(pos: Position, msg: String) = reporter.echo(fixPosition(pos), msg) + def fixPosition(pos: Position): Position = pos match { + case NoPosition => nextEnclosing(_.tree.pos != NoPosition).tree.pos + case _ => pos + } def deprecationWarning(pos: Position, sym: Symbol, msg: String): Unit = - currentRun.reporting.deprecationWarning(pos, sym, msg) + currentRun.reporting.deprecationWarning(fixPosition(pos), sym, msg) def deprecationWarning(pos: Position, sym: Symbol): Unit = - currentRun.reporting.deprecationWarning(pos, sym) // TODO: allow this to escalate to an error, and implicit search will ignore deprecated implicits + currentRun.reporting.deprecationWarning(fixPosition(pos), sym) // TODO: allow this to escalate to an error, and implicit search will ignore deprecated implicits def featureWarning(pos: Position, featureName: String, featureDesc: String, featureTrait: Symbol, construct: => String = "", required: Boolean): Unit = - currentRun.reporting.featureWarning(pos, featureName, featureDesc, featureTrait, construct, required) + currentRun.reporting.featureWarning(fixPosition(pos), featureName, featureDesc, featureTrait, construct, required) // nextOuter determines which context is searched next for implicits @@ -1204,7 +1208,7 @@ trait Contexts { self: Analyzer => type Error = AbsTypeError type Warning = (Position, String) - def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(err.errPos, addDiagString(err.errMsg)) + def issue(err: AbsTypeError)(implicit context: Context): Unit = handleError(context.fixPosition(err.errPos), addDiagString(err.errMsg)) protected def handleError(pos: Position, msg: String): Unit protected def handleSuppressedAmbiguous(err: AbsAmbiguousTypeError): Unit = () @@ -1221,7 +1225,7 @@ trait Contexts { self: Analyzer => * - else, let this context reporter decide */ final def issueAmbiguousError(err: AbsAmbiguousTypeError)(implicit context: Context): Unit = - if (context.ambiguousErrors) reporter.error(err.errPos, addDiagString(err.errMsg)) // force reporting... see TODO above + if (context.ambiguousErrors) reporter.error(context.fixPosition(err.errPos), addDiagString(err.errMsg)) // force reporting... see TODO above else handleSuppressedAmbiguous(err) @inline final def withFreshErrorBuffer[T](expr: => T): T = { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 2fb3ae41e2..383baad2ad 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -920,7 +920,7 @@ trait Implicits { /** Returns all eligible ImplicitInfos and their SearchResults in a map. */ - def findAll() = linkedMapFrom(eligible)(typedImplicit(_, ptChecked = false, isLocalToCallsite)) + def findAll() = linkedMapFrom(eligible)(x => try typedImplicit(x, ptChecked = false, isLocalToCallsite) finally context.reporter.clearAll()) /** Returns the SearchResult of the best match. */ diff --git a/src/compiler/scala/tools/nsc/util/DocStrings.scala b/src/compiler/scala/tools/nsc/util/DocStrings.scala index 501546b8f6..501546b8f6 100755..100644 --- a/src/compiler/scala/tools/nsc/util/DocStrings.scala +++ b/src/compiler/scala/tools/nsc/util/DocStrings.scala diff --git a/src/interactive/scala/tools/nsc/interactive/CompilerControl.scala b/src/interactive/scala/tools/nsc/interactive/CompilerControl.scala index 9caebb711d..586f011429 100644 --- a/src/interactive/scala/tools/nsc/interactive/CompilerControl.scala +++ b/src/interactive/scala/tools/nsc/interactive/CompilerControl.scala @@ -268,10 +268,12 @@ trait CompilerControl { self: Global => /** Info given for every member found by completion */ abstract class Member { + def prefix: Type val sym: Symbol val tpe: Type val accessible: Boolean def implicitlyAdded = false + def symNameDropLocal: Name = sym.name.dropLocal private def accessible_s = if (accessible) "" else "[inaccessible] " def forceInfoString = { @@ -288,6 +290,8 @@ trait CompilerControl { self: Global => accessible: Boolean, inherited: Boolean, viaView: Symbol) extends Member { + // should be a case class parameter, but added as a var instead to preserve compatibility with the IDE + var prefix: Type = NoType override def implicitlyAdded = viaView != NoSymbol } @@ -295,7 +299,10 @@ trait CompilerControl { self: Global => sym: Symbol, tpe: Type, accessible: Boolean, - viaImport: Tree) extends Member + viaImport: Tree) extends Member { + // should be a case class parameter, but added as a var instead to preserve compatibility with the IDE + var prefix: Type = NoType + } // items that get sent to scheduler diff --git a/src/interactive/scala/tools/nsc/interactive/Global.scala b/src/interactive/scala/tools/nsc/interactive/Global.scala index e79c1a01ab..5298a6ae57 100644 --- a/src/interactive/scala/tools/nsc/interactive/Global.scala +++ b/src/interactive/scala/tools/nsc/interactive/Global.scala @@ -21,6 +21,7 @@ import scala.tools.nsc.typechecker.Typers import scala.util.control.Breaks._ import java.util.concurrent.ConcurrentHashMap import scala.collection.JavaConverters.mapAsScalaMapConverter +import scala.reflect.internal.Chars.isIdentifierStart /** * This trait allows the IDE to have an instance of the PC that @@ -122,6 +123,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") val debugIDE: Boolean = settings.YpresentationDebug.value val verboseIDE: Boolean = settings.YpresentationVerbose.value + private val anyThread: Boolean = settings.YpresentationAnyThread.value private def replayName = settings.YpresentationReplay.value private def logName = settings.YpresentationLog.value @@ -521,7 +523,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") */ @elidable(elidable.WARNING) override def assertCorrectThread() { - assert(initializing || onCompilerThread, + assert(initializing || anyThread || onCompilerThread, "Race condition detected: You are running a presentation compiler method outside the PC thread.[phase: %s]".format(globalPhase) + " Please file a ticket with the current stack trace at https://www.assembla.com/spaces/scala-ide/support/tickets") } @@ -650,7 +652,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") /** Make sure unit is typechecked */ - private def typeCheck(unit: RichCompilationUnit) { + private[scala] def typeCheck(unit: RichCompilationUnit) { debugLog("type checking: "+unit) parseAndEnter(unit) unit.status = PartiallyChecked @@ -781,7 +783,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } /** A fully attributed tree located at position `pos` */ - private[interactive] def typedTreeAt(pos: Position): Tree = getUnit(pos.source) match { + private[scala] def typedTreeAt(pos: Position): Tree = getUnit(pos.source) match { case None => reloadSources(List(pos.source)) try typedTreeAt(pos) @@ -987,7 +989,7 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") def add(sym: Symbol, pre: Type, implicitlyAdded: Boolean)(toMember: (Symbol, Type) => M) { if ((sym.isGetter || sym.isSetter) && sym.accessed != NoSymbol) { add(sym.accessed, pre, implicitlyAdded)(toMember) - } else if (!sym.name.decodedName.containsName("$") && !sym.isSynthetic && sym.hasRawInfo) { + } else if (!sym.name.decodedName.containsName("$") && !sym.isError && !sym.isArtifact && sym.hasRawInfo) { val symtpe = pre.memberType(sym) onTypeError ErrorType matching(sym, symtpe, this(sym.name)) match { case Some(m) => @@ -1019,10 +1021,12 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") def addScopeMember(sym: Symbol, pre: Type, viaImport: Tree) = locals.add(sym, pre, implicitlyAdded = false) { (s, st) => // imported val and var are always marked as inaccessible, but they could be accessed through their getters. SI-7995 - if (s.hasGetter) + val member = if (s.hasGetter) new ScopeMember(s, st, context.isAccessible(s.getter, pre, superAccess = false), viaImport) else new ScopeMember(s, st, context.isAccessible(s, pre, superAccess = false), viaImport) + member.prefix = pre + member } def localsToEnclosing() = { enclosing.addNonShadowed(locals) @@ -1089,10 +1093,13 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") def addTypeMember(sym: Symbol, pre: Type, inherited: Boolean, viaView: Symbol) = { val implicitlyAdded = viaView != NoSymbol members.add(sym, pre, implicitlyAdded) { (s, st) => - new TypeMember(s, st, + val result = new TypeMember(s, st, context.isAccessible(if (s.hasGetter) s.getterIn(s.owner) else s, pre, superAccess && !implicitlyAdded), inherited, viaView) + result.prefix = pre + result + } } @@ -1136,6 +1143,112 @@ class Global(settings: Settings, _reporter: Reporter, projectName: String = "") } } + sealed abstract class CompletionResult { + type M <: Member + def results: List[M] + /** The (possibly partial) name detected that precedes the cursor */ + def name: Name + /** Cursor Offset - positionDelta == position of the start of the name */ + def positionDelta: Int + def matchingResults(nameMatcher: (Name) => Name => Boolean = entered => candidate => candidate.startsWith(entered)): List[M] = { + val enteredName = if (name == nme.ERROR) nme.EMPTY else name + val matcher = nameMatcher(enteredName) + results filter { (member: Member) => + val symbol = member.sym + def isStable = member.tpe.isStable || member.sym.isStable || member.sym.getterIn(member.sym.owner).isStable + def isJunk = symbol.name.isEmpty || !isIdentifierStart(member.sym.name.charAt(0)) // e.g. <byname> + !isJunk && member.accessible && !symbol.isConstructor && (name.isEmpty || matcher(member.sym.name) && (symbol.name.isTermName == name.isTermName || name.isTypeName && isStable)) + } + } + } + object CompletionResult { + final case class ScopeMembers(positionDelta: Int, results: List[ScopeMember], name: Name) extends CompletionResult { + type M = ScopeMember + } + final case class TypeMembers(positionDelta: Int, qualifier: Tree, tree: Tree, results: List[TypeMember], name: Name) extends CompletionResult { + type M = TypeMember + } + case object NoResults extends CompletionResult { + override def results = Nil + override def name = nme.EMPTY + override def positionDelta = 0 + + } + private val CamelRegex = "([A-Z][^A-Z]*)".r + private def camelComponents(s: String): List[String] = { + CamelRegex.findAllIn("X" + s).toList match { case head :: tail => head.drop(1) :: tail; case Nil => Nil } + } + def camelMatch(entered: Name): Name => Boolean = { + val enteredS = entered.toString + val enteredLowercaseSet = enteredS.toLowerCase().toSet + + (candidate: Name) => { + def candidateChunks = camelComponents(candidate.toString) + // Loosely based on IntelliJ's autocompletion: the user can just write everything in + // lowercase, as we'll let `isl` match `GenIndexedSeqLike` or `isLovely`. + def lenientMatch(entered: String, candidate: List[String], matchCount: Int): Boolean = { + candidate match { + case Nil => entered.isEmpty && matchCount > 0 + case head :: tail => + val enteredAlternatives = Set(entered, entered.capitalize) + head.inits.filter(_.length <= entered.length).exists(init => + enteredAlternatives.exists(entered => + lenientMatch(entered.stripPrefix(init), tail, matchCount + (if (init.isEmpty) 0 else 1)) + ) + ) + } + } + val containsAllEnteredChars = { + // Trying to rule out some candidates quickly before the more expensive `lenientMatch` + val candidateLowercaseSet = candidate.toString.toLowerCase().toSet + enteredLowercaseSet.diff(candidateLowercaseSet).isEmpty + } + containsAllEnteredChars && lenientMatch(enteredS, candidateChunks, 0) + } + } + } + + final def completionsAt(pos: Position): CompletionResult = { + val focus1: Tree = typedTreeAt(pos) + def typeCompletions(tree: Tree, qual: Tree, nameStart: Int, name: Name): CompletionResult = { + val qualPos = qual.pos + val allTypeMembers = typeMembers(qualPos).toList.flatten + val positionDelta: Int = pos.start - nameStart + val subName: Name = name.newName(new String(pos.source.content, nameStart, pos.start - nameStart)).encodedName + CompletionResult.TypeMembers(positionDelta, qual, tree, allTypeMembers, subName) + } + focus1 match { + case imp@Import(i @ Ident(name), head :: Nil) if head.name == nme.ERROR => + val allMembers = scopeMembers(pos) + val nameStart = i.pos.start + val positionDelta: Int = pos.start - nameStart + val subName = name.subName(0, pos.start - i.pos.start) + CompletionResult.ScopeMembers(positionDelta, allMembers, subName) + case imp@Import(qual, selectors) => + selectors.reverseIterator.find(_.namePos <= pos.start) match { + case None => CompletionResult.NoResults + case Some(selector) => + typeCompletions(imp, qual, selector.namePos, selector.name) + } + case sel@Select(qual, name) => + val qualPos = qual.pos + def fallback = qualPos.end + 2 + val source = pos.source + val nameStart: Int = (qualPos.end + 1 until focus1.pos.end).find(p => + source.identifier(source.position(p)).exists(_.length > 0) + ).getOrElse(fallback) + typeCompletions(sel, qual, nameStart, name) + case Ident(name) => + val allMembers = scopeMembers(pos) + val positionDelta: Int = pos.start - focus1.pos.start + val subName = name.subName(0, positionDelta) + CompletionResult.ScopeMembers(positionDelta, allMembers, subName) + case _ => + CompletionResult.NoResults + } + } + + /** Implements CompilerControl.askLoadedTyped */ private[interactive] def waitLoadedTyped(source: SourceFile, response: Response[Tree], keepLoaded: Boolean = false, onSameThread: Boolean = true) { getUnit(source) match { diff --git a/src/library-aux/scala/Any.scala b/src/library-aux/scala/Any.scala index 8caf0c5c0e..e6ed46740e 100644 --- a/src/library-aux/scala/Any.scala +++ b/src/library-aux/scala/Any.scala @@ -27,7 +27,7 @@ package scala * w.print() * }}} * - * See the [[http://docs.scala-lang.org/sips/completed/value-classes.html value classes guide]] for more + * See the [[http://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]] for more * details on the interplay of universal traits and value classes. */ abstract class Any { diff --git a/src/library/scala/AnyVal.scala b/src/library/scala/AnyVal.scala index fb3d213e19..e861860196 100644 --- a/src/library/scala/AnyVal.scala +++ b/src/library/scala/AnyVal.scala @@ -48,9 +48,7 @@ package scala * * It's important to note that user-defined value classes are limited, and in some circumstances, * still must allocate a value class instance at runtime. These limitations and circumstances are - * explained in greater detail in the [[http://docs.scala-lang.org/overviews/core/value-classes.html Value Classes Guide]] - * as well as in [[http://docs.scala-lang.org/sips/completed/value-classes.html SIP-15: Value Classes]], - * the Scala Improvement Proposal. + * explained in greater detail in the [[http://docs.scala-lang.org/overviews/core/value-classes.html Value Classes and Universal Traits]]. */ abstract class AnyVal extends Any { def getClass(): Class[_ <: AnyVal] = null diff --git a/src/library/scala/collection/IndexedSeqOptimized.scala b/src/library/scala/collection/IndexedSeqOptimized.scala index a7e06b4d1a..a7e06b4d1a 100755..100644 --- a/src/library/scala/collection/IndexedSeqOptimized.scala +++ b/src/library/scala/collection/IndexedSeqOptimized.scala diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala index e2c271145d..ff10fb44d7 100644 --- a/src/library/scala/collection/Iterator.scala +++ b/src/library/scala/collection/Iterator.scala @@ -431,8 +431,16 @@ trait Iterator[+A] extends TraversableOnce[A] { */ def flatMap[B](f: A => GenTraversableOnce[B]): Iterator[B] = new AbstractIterator[B] { private var cur: Iterator[B] = empty - def hasNext: Boolean = - cur.hasNext || self.hasNext && { cur = f(self.next()).toIterator; hasNext } + private def nextCur() { cur = f(self.next()).toIterator } + def hasNext: Boolean = { + // Equivalent to cur.hasNext || self.hasNext && { nextCur(); hasNext } + // but slightly shorter bytecode (better JVM inlining!) + while (!cur.hasNext) { + if (!self.hasNext) return false + nextCur() + } + true + } def next(): B = (if (hasNext) cur else empty).next() } @@ -444,6 +452,7 @@ trait Iterator[+A] extends TraversableOnce[A] { * @note Reuse: $consumesAndProducesIterator */ def filter(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { + // TODO 2.12 - Make a full-fledged FilterImpl that will reverse sense of p private var hd: A = _ private var hdDefined: Boolean = false @@ -509,13 +518,27 @@ trait Iterator[+A] extends TraversableOnce[A] { * @note Reuse: $consumesAndProducesIterator */ @migration("`collect` has changed. The previous behavior can be reproduced with `toSeq`.", "2.8.0") - def collect[B](pf: PartialFunction[A, B]): Iterator[B] = { - val self = buffered - new AbstractIterator[B] { - private def skip() = while (self.hasNext && !pf.isDefinedAt(self.head)) self.next() - def hasNext = { skip(); self.hasNext } - def next() = { skip(); pf(self.next()) } + def collect[B](pf: PartialFunction[A, B]): Iterator[B] = new AbstractIterator[B] { + // Manually buffer to avoid extra layer of wrapping with buffered + private[this] var hd: A = _ + + // Little state machine to keep track of where we are + // Seek = 0; Found = 1; Empty = -1 + // Not in vals because scalac won't make them static (@inline def only works with -optimize) + // BE REALLY CAREFUL TO KEEP COMMENTS AND NUMBERS IN SYNC! + private[this] var status = 0/*Seek*/ + + def hasNext = { + while (status == 0/*Seek*/) { + if (self.hasNext) { + hd = self.next() + if (pf.isDefinedAt(hd)) status = 1/*Found*/ + } + else status = -1/*Empty*/ + } + status == 1/*Found*/ } + def next() = if (hasNext) { status = 0/*Seek*/; pf(hd) } else Iterator.empty.next() } /** Produces a collection containing cumulative results of applying the @@ -617,33 +640,105 @@ trait Iterator[+A] extends TraversableOnce[A] { * @note Reuse: $consumesOneAndProducesTwoIterators */ def span(p: A => Boolean): (Iterator[A], Iterator[A]) = { - val self = buffered - - // Must be a named class to avoid structural call to finish from trailing iterator + /* + * Giving a name to following iterator (as opposed to trailing) because + * anonymous class is represented as a structural type that trailing + * iterator is referring (the finish() method) and thus triggering + * handling of structural calls. It's not what's intended here. + */ class Leading extends AbstractIterator[A] { - private val drained = new mutable.Queue[A] - private var finished = false - def finish(): Unit = { - require(!finished) - finished = true - while (selfish) drained += self.next + var lookahead: mutable.Queue[A] = null + var hd: A = _ + /* Status is kept with magic numbers + * 1 means next element is in hd and we're still reading into this iterator + * 0 means we're still reading but haven't found a next element + * -1 means we are done reading into the iterator, so we must rely on lookahead + * -2 means we are done but have saved hd for the other iterator to use as its first element + */ + var status = 0 + private def store(a: A) { + if (lookahead == null) lookahead = new mutable.Queue[A] + lookahead += a + } + def hasNext = { + if (status < 0) (lookahead ne null) && lookahead.nonEmpty + else if (status > 0) true + else { + if (self.hasNext) { + hd = self.next() + status = if (p(hd)) 1 else -2 + } + else status = -1 + status > 0 + } } - private def selfish = self.hasNext && p(self.head) - def hasNext = if (finished) drained.nonEmpty else selfish def next() = { - if (finished) drained.dequeue() - else if (selfish) self.next() + if (hasNext) { + if (status == 1) { status = 0; hd } + else lookahead.dequeue() + } else empty.next() } + def finish(): Boolean = { + if (status == -1) false + else if (status == -2) { + status = -1 + true + } + else { + if (status == 1) store(hd) + while (self.hasNext) { + val a = self.next() + if (p(a)) store(a) + else { + hd = a + status = -1 + return true + } + } + false + } + } } + val leading = new Leading + val trailing = new AbstractIterator[A] { - private lazy val it = { - leading.finish() - self + private[this] var myLeading = leading + /* Status flags meanings: + * -1 not yet accesssed + * 0 single element waiting in leading + * 1 defer to self + */ + private[this] var status = -1 + def hasNext = { + if (status > 0) self.hasNext + else { + if (status == 0) true + else if (myLeading.finish()) { + status = 0 + true + } + else { + status = 1 + myLeading = null + self.hasNext + } + } + } + def next() = { + if (hasNext) { + if (status > 0) self.next() + else { + status = 1 + val ans = myLeading.hd + myLeading = null + ans + } + } + else Iterator.empty.next() } - def hasNext = it.hasNext - def next() = it.next() + override def toString = "unknown-if-empty iterator" } @@ -657,18 +752,35 @@ trait Iterator[+A] extends TraversableOnce[A] { * @return an iterator consisting of the remaining elements * @note Reuse: $consumesAndProducesIterator */ - def dropWhile(p: A => Boolean): Iterator[A] = { - val self = buffered - new AbstractIterator[A] { - var dropped = false - private def skip() = - if (!dropped) { - while (self.hasNext && p(self.head)) self.next() - dropped = true + def dropWhile(p: A => Boolean): Iterator[A] = new AbstractIterator[A] { + // Magic value: -1 = hasn't dropped, 0 = found first, 1 = defer to parent iterator + private[this] var status = -1 + // Local buffering to avoid double-wrap with .buffered + private[this] var fst: A = _ + def hasNext: Boolean = + if (status == 1) self.hasNext + else if (status == 0) true + else { + while (self.hasNext) { + val a = self.next() + if (!p(a)) { + fst = a + status = 0 + return true + } } - def hasNext = { skip(); self.hasNext } - def next() = { skip(); self.next() } - } + status = 1 + false + } + def next() = + if (hasNext) { + if (status == 1) self.next() + else { + status = 1 + fst + } + } + else Iterator.empty.next() } /** Creates an iterator formed from this iterator and another iterator @@ -816,7 +928,7 @@ trait Iterator[+A] extends TraversableOnce[A] { * is equal (as determined by `==`) to `elem`, `false` otherwise. * @note Reuse: $consumesIterator */ - def contains(elem: Any): Boolean = exists(_ == elem) + def contains(elem: Any): Boolean = exists(_ == elem) // Note--this seems faster than manual inlining! /** Finds the first value produced by the iterator satisfying a * predicate, if any. @@ -828,12 +940,11 @@ trait Iterator[+A] extends TraversableOnce[A] { * @note Reuse: $consumesIterator */ def find(p: A => Boolean): Option[A] = { - var res: Option[A] = None - while (res.isEmpty && hasNext) { - val e = next() - if (p(e)) res = Some(e) + while (hasNext) { + val a = next() + if (p(a)) return Some(a) } - res + None } /** Returns the index of the first produced value satisfying a predicate, or -1. @@ -863,15 +974,11 @@ trait Iterator[+A] extends TraversableOnce[A] { i += 1 } - var found = false - while (!found && hasNext) { - if (p(next())) { - found = true - } else { - i += 1 - } + while (hasNext) { + if (p(next())) return i + i += 1 } - if (found) i else -1 + -1 } /** Returns the index of the first occurrence of the specified @@ -903,15 +1010,11 @@ trait Iterator[+A] extends TraversableOnce[A] { i += 1 } - var found = false - while (!found && hasNext) { - if (next() == elem) { - found = true - } else { - i += 1 - } + while (hasNext) { + if (next() == elem) return i + i += 1 } - if (found) i else -1 + -1 } /** Creates a buffered iterator from this iterator. diff --git a/src/library/scala/collection/JavaConverters.scala b/src/library/scala/collection/JavaConverters.scala index 875f6e1c02..875f6e1c02 100755..100644 --- a/src/library/scala/collection/JavaConverters.scala +++ b/src/library/scala/collection/JavaConverters.scala diff --git a/src/library/scala/collection/LinearSeqOptimized.scala b/src/library/scala/collection/LinearSeqOptimized.scala index 571d58a3f3..571d58a3f3 100755..100644 --- a/src/library/scala/collection/LinearSeqOptimized.scala +++ b/src/library/scala/collection/LinearSeqOptimized.scala diff --git a/src/library/scala/collection/generic/FilterMonadic.scala b/src/library/scala/collection/generic/FilterMonadic.scala index 8aefbdb926..8aefbdb926 100755..100644 --- a/src/library/scala/collection/generic/FilterMonadic.scala +++ b/src/library/scala/collection/generic/FilterMonadic.scala diff --git a/src/library/scala/collection/generic/HasNewBuilder.scala b/src/library/scala/collection/generic/HasNewBuilder.scala index aa0ce6698d..aa0ce6698d 100755..100644 --- a/src/library/scala/collection/generic/HasNewBuilder.scala +++ b/src/library/scala/collection/generic/HasNewBuilder.scala diff --git a/src/library/scala/collection/immutable/DefaultMap.scala b/src/library/scala/collection/immutable/DefaultMap.scala index e9b277b9c4..e9b277b9c4 100755..100644 --- a/src/library/scala/collection/immutable/DefaultMap.scala +++ b/src/library/scala/collection/immutable/DefaultMap.scala diff --git a/src/library/scala/collection/immutable/IntMap.scala b/src/library/scala/collection/immutable/IntMap.scala index 8991d0b75a..cb6196e130 100644 --- a/src/library/scala/collection/immutable/IntMap.scala +++ b/src/library/scala/collection/immutable/IntMap.scala @@ -146,7 +146,7 @@ private[immutable] class IntMapKeyIterator[V](it: IntMap[V]) extends IntMapItera import IntMap._ /** Specialised immutable map structure for integer keys, based on - * <a href="http://citeseer.ist.psu.edu/okasaki98fast.html">Fast Mergeable Integer Maps</a> + * [[http://ittc.ku.edu/~andygill/papers/IntMap98.pdf Fast Mergeable Integer Maps]] * by Okasaki and Gill. Essentially a trie based on binary digits of the integers. * * '''Note:''' This class is as of 2.8 largely superseded by HashMap. diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index eb095dbbc2..66150c1a13 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -16,7 +16,7 @@ import scala.annotation.tailrec import java.io.{ObjectOutputStream, ObjectInputStream} /** A class for immutable linked lists representing ordered collections - * of elements of type. + * of elements of type `A`. * * This class comes with two implementing case classes `scala.Nil` * and `scala.::` that implement the abstract members `isEmpty`, diff --git a/src/library/scala/collection/immutable/NumericRange.scala b/src/library/scala/collection/immutable/NumericRange.scala index 28e56a6d87..11603a118b 100644 --- a/src/library/scala/collection/immutable/NumericRange.scala +++ b/src/library/scala/collection/immutable/NumericRange.scala @@ -175,34 +175,68 @@ extends AbstractSeq[T] with IndexedSeq[T] with Serializable { catch { case _: ClassCastException => false } final override def sum[B >: T](implicit num: Numeric[B]): B = { - // arithmetic series formula can be used for regular addition - if ((num eq scala.math.Numeric.IntIsIntegral)|| - (num eq scala.math.Numeric.BigIntIsIntegral)|| - (num eq scala.math.Numeric.ShortIsIntegral)|| - (num eq scala.math.Numeric.ByteIsIntegral)|| - (num eq scala.math.Numeric.CharIsIntegral)|| - (num eq scala.math.Numeric.LongIsIntegral)|| - (num eq scala.math.Numeric.FloatAsIfIntegral)|| - (num eq scala.math.Numeric.BigDecimalIsFractional)|| - (num eq scala.math.Numeric.DoubleAsIfIntegral)) { - val numAsIntegral = num.asInstanceOf[Integral[B]] - import numAsIntegral._ - if (isEmpty) num fromInt 0 - else if (numRangeElements == 1) head - else ((num fromInt numRangeElements) * (head + last) / (num fromInt 2)) - } else { - // user provided custom Numeric, we cannot rely on arithmetic series formula - if (isEmpty) num.zero + if (isEmpty) num.zero + else if (numRangeElements == 1) head + else { + // If there is no overflow, use arithmetic series formula + // a + ... (n terms total) ... + b = n*(a+b)/2 + if ((num eq scala.math.Numeric.IntIsIntegral)|| + (num eq scala.math.Numeric.ShortIsIntegral)|| + (num eq scala.math.Numeric.ByteIsIntegral)|| + (num eq scala.math.Numeric.CharIsIntegral)) { + // We can do math with no overflow in a Long--easy + val exact = (numRangeElements * ((num toLong head) + (num toInt last))) / 2 + num fromInt exact.toInt + } + else if (num eq scala.math.Numeric.LongIsIntegral) { + // Uh-oh, might be overflow, so we have to divide before we overflow. + // Either numRangeElements or (head + last) must be even, so divide the even one before multiplying + val a = head.toLong + val b = last.toLong + val ans = + if ((numRangeElements & 1) == 0) (numRangeElements / 2) * (a + b) + else numRangeElements * { + // Sum is even, but we might overflow it, so divide in pieces and add back remainder + val ha = a/2 + val hb = b/2 + ha + hb + ((a - 2*ha) + (b - 2*hb)) / 2 + } + ans.asInstanceOf[B] + } + else if ((num eq scala.math.Numeric.FloatAsIfIntegral) || + (num eq scala.math.Numeric.DoubleAsIfIntegral)) { + // Try to compute sum with reasonable accuracy, avoiding over/underflow + val numAsIntegral = num.asInstanceOf[Integral[B]] + import numAsIntegral._ + val a = math.abs(head.toDouble) + val b = math.abs(last.toDouble) + val two = num fromInt 2 + val nre = num fromInt numRangeElements + if (a > 1e38 || b > 1e38) nre * ((head / two) + (last / two)) // Compute in parts to avoid Infinity if possible + else (nre / two) * (head + last) // Don't need to worry about infinity; this will be more accurate and avoid underflow + } + else if ((num eq scala.math.Numeric.BigIntIsIntegral) || + (num eq scala.math.Numeric.BigDecimalIsFractional)) { + // No overflow, so we can use arithmetic series formula directly + // (not going to worry about running out of memory) + val numAsIntegral = num.asInstanceOf[Integral[B]] + import numAsIntegral._ + ((num fromInt numRangeElements) * (head + last)) / (num fromInt 2) + } else { - var acc = num.zero - var i = head - var idx = 0 - while(idx < length) { - acc = num.plus(acc, i) - i = i + step - idx = idx + 1 + // User provided custom Numeric, so we cannot rely on arithmetic series formula (e.g. won't work on something like Z_6) + if (isEmpty) num.zero + else { + var acc = num.zero + var i = head + var idx = 0 + while(idx < length) { + acc = num.plus(acc, i) + i = i + step + idx = idx + 1 + } + acc } - acc } } } diff --git a/src/library/scala/collection/immutable/PagedSeq.scala b/src/library/scala/collection/immutable/PagedSeq.scala index 8910ee16b9..c8d0b00327 100644 --- a/src/library/scala/collection/immutable/PagedSeq.scala +++ b/src/library/scala/collection/immutable/PagedSeq.scala @@ -190,8 +190,8 @@ extends scala.collection.AbstractSeq[T] val e = if (_end == UndeterminedEnd) _end else start + _end var f = first1 while (f.end <= s && !f.isLast) { - if (f.next eq null) f.addMore(more) - f = f.next + if (f.next eq null) f = f.addMore(more) + else f = f.next } // Warning -- not refining `more` means that slices can freely request and obtain // data outside of their slice. This is part of the design of PagedSeq diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala index 984ea7ba50..fb7dd4cfbf 100644 --- a/src/library/scala/collection/immutable/Range.scala +++ b/src/library/scala/collection/immutable/Range.scala @@ -153,19 +153,15 @@ extends scala.collection.AbstractSeq[Int] } @inline final override def foreach[@specialized(Unit) U](f: Int => U) { - validateMaxLength() - val isCommonCase = (start != Int.MinValue || end != Int.MinValue) - var i = start - var count = 0 - val terminal = terminalElement - val step = this.step - while( - if(isCommonCase) { i != terminal } - else { count < numRangeElements } - ) { - f(i) - count += 1 - i += step + // Implementation chosen on the basis of favorable microbenchmarks + // Note--initialization catches step == 0 so we don't need to here + if (!isEmpty) { + var i = start + while (true) { + f(i) + if (i == lastElement) return + i += step + } } } @@ -364,18 +360,19 @@ extends scala.collection.AbstractSeq[Int] // this is normal integer range with usual addition. arithmetic series formula can be used if (isEmpty) 0 else if (numRangeElements == 1) head - else (numRangeElements.toLong * (head + last) / 2).toInt + else ((numRangeElements * (head.toLong + last)) / 2).toInt } else { // user provided custom Numeric, we cannot rely on arithmetic series formula if (isEmpty) num.toInt(num.zero) else { var acc = num.zero var i = head - while(i != terminalElement) { + while (true) { acc = num.plus(acc, i) + if (i == lastElement) return num.toInt(acc) i = i + step } - num.toInt(acc) + 0 // Never hit this--just to satisfy compiler since it doesn't know while(true) has type Nothing } } } diff --git a/src/library/scala/collection/immutable/RedBlackTree.scala b/src/library/scala/collection/immutable/RedBlackTree.scala index 0dad106b29..7e8cfcc902 100644 --- a/src/library/scala/collection/immutable/RedBlackTree.scala +++ b/src/library/scala/collection/immutable/RedBlackTree.scala @@ -168,7 +168,8 @@ object RedBlackTree { } /* Based on Stefan Kahrs' Haskell version of Okasaki's Red&Black Trees - * http://www.cse.unsw.edu.au/~dons/data/RedBlackTree.html */ + * Constructing Red-Black Trees, Ralf Hinze: http://www.cs.ox.ac.uk/ralf.hinze/publications/WAAAPL99b.ps.gz + * Red-Black Trees in a Functional Setting, Chris Okasaki: https://wiki.rice.edu/confluence/download/attachments/2761212/Okasaki-Red-Black.pdf */ private[this] def del[A, B](tree: Tree[A, B], k: A)(implicit ordering: Ordering[A]): Tree[A, B] = if (tree eq null) null else { def balance(x: A, xv: B, tl: Tree[A, B], tr: Tree[A, B]) = if (isRedTree(tl)) { if (isRedTree(tr)) { diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index 46d5d0c69c..8bb581d44c 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -1156,8 +1156,6 @@ private[immutable] trait VectorPointer[T] { if (depth == 3) { display3 = new Array(32) display3((oldIndex >> 15) & 31) = display2 - display2 = new Array(32) - display1 = new Array(32) depth +=1 } display2 = display3((newIndex >> 15) & 31).asInstanceOf[Array[AnyRef]] @@ -1170,9 +1168,6 @@ private[immutable] trait VectorPointer[T] { if (depth == 4) { display4 = new Array(32) display4((oldIndex >> 20) & 31) = display3 - display3 = new Array(32) - display2 = new Array(32) - display1 = new Array(32) depth +=1 } display3 = display4((newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] @@ -1187,13 +1182,9 @@ private[immutable] trait VectorPointer[T] { if (depth == 5) { display5 = new Array(32) display5((oldIndex >> 25) & 31) = display4 - display4 = new Array(32) - display3 = new Array(32) - display2 = new Array(32) - display1 = new Array(32) depth +=1 } - display4 = display5((newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] + display4 = display5((newIndex >> 25) & 31).asInstanceOf[Array[AnyRef]] if (display4 == null) display4 = new Array(32) display3 = display4((newIndex >> 20) & 31).asInstanceOf[Array[AnyRef]] if (display3 == null) display3 = new Array(32) diff --git a/src/library/scala/collection/mutable/IndexedSeqOptimized.scala b/src/library/scala/collection/mutable/IndexedSeqOptimized.scala index 09f0712862..09f0712862 100755..100644 --- a/src/library/scala/collection/mutable/IndexedSeqOptimized.scala +++ b/src/library/scala/collection/mutable/IndexedSeqOptimized.scala diff --git a/src/library/scala/collection/mutable/Queue.scala b/src/library/scala/collection/mutable/Queue.scala index 03d387a535..ad60173b64 100644 --- a/src/library/scala/collection/mutable/Queue.scala +++ b/src/library/scala/collection/mutable/Queue.scala @@ -21,7 +21,7 @@ import generic._ * @author Martin Odersky * @version 2.8 * @since 1 - * @see [[http://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#mutable_queues "Scala's Collection Library overview"]] + * @see [[http://docs.scala-lang.org/overviews/collections/concrete-mutable-collection-classes.html#queues "Scala's Collection Library overview"]] * section on `Queues` for more information. * * @define Coll `mutable.Queue` diff --git a/src/library/scala/collection/package.scala b/src/library/scala/collection/package.scala index 6a2b6de75a..13fe7a79c4 100644 --- a/src/library/scala/collection/package.scala +++ b/src/library/scala/collection/package.scala @@ -69,6 +69,9 @@ package scala * characteristics which are described * in [[http://docs.scala-lang.org/overviews/collections/performance-characteristics.html the guide]]. * + * The concrete parallel collections also have specific performance characteristics which are + * described in [[http://docs.scala-lang.org/overviews/parallel-collections/concrete-parallel-collections.html#performance-characteristics the parallel collections guide]] + * * === Converting between Java Collections === * * The `JavaConversions` object provides implicit defs that will allow mostly seamless integration diff --git a/src/library/scala/collection/readme-if-you-want-to-add-something.txt b/src/library/scala/collection/readme-if-you-want-to-add-something.txt index 6700cb7b68..6700cb7b68 100755..100644 --- a/src/library/scala/collection/readme-if-you-want-to-add-something.txt +++ b/src/library/scala/collection/readme-if-you-want-to-add-something.txt diff --git a/src/library/scala/io/Codec.scala b/src/library/scala/io/Codec.scala index 2a41e25b01..7cb7858b36 100644 --- a/src/library/scala/io/Codec.scala +++ b/src/library/scala/io/Codec.scala @@ -21,7 +21,7 @@ import scala.language.implicitConversions // XML: optional encoding parameter. // <?xml version="1.0" encoding="ISO8859-1" ?> // -// MacRoman vs. UTF-8: see http://jira.codehaus.org/browse/JRUBY-3576 +// MacRoman vs. UTF-8: see http://osdir.com/ml/lang-jruby-devel/2009-04/msg00071.html // -Dfile.encoding: see http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4375816 /** A class for character encoding/decoding preferences. diff --git a/src/library/scala/reflect/NameTransformer.scala b/src/library/scala/reflect/NameTransformer.scala index a8430548f5..a8430548f5 100755..100644 --- a/src/library/scala/reflect/NameTransformer.scala +++ b/src/library/scala/reflect/NameTransformer.scala diff --git a/src/library/scala/runtime/TraitSetter.java b/src/library/scala/runtime/TraitSetter.java index d9907c0ac0..d9907c0ac0 100755..100644 --- a/src/library/scala/runtime/TraitSetter.java +++ b/src/library/scala/runtime/TraitSetter.java diff --git a/src/library/scala/runtime/VolatileBooleanRef.java b/src/library/scala/runtime/VolatileBooleanRef.java index ef5b691118..ef5b691118 100755..100644 --- a/src/library/scala/runtime/VolatileBooleanRef.java +++ b/src/library/scala/runtime/VolatileBooleanRef.java diff --git a/src/library/scala/runtime/VolatileByteRef.java b/src/library/scala/runtime/VolatileByteRef.java index d792b0a386..d792b0a386 100755..100644 --- a/src/library/scala/runtime/VolatileByteRef.java +++ b/src/library/scala/runtime/VolatileByteRef.java diff --git a/src/library/scala/runtime/VolatileCharRef.java b/src/library/scala/runtime/VolatileCharRef.java index 555b171283..555b171283 100755..100644 --- a/src/library/scala/runtime/VolatileCharRef.java +++ b/src/library/scala/runtime/VolatileCharRef.java diff --git a/src/library/scala/runtime/VolatileDoubleRef.java b/src/library/scala/runtime/VolatileDoubleRef.java index 1932055c6a..1932055c6a 100755..100644 --- a/src/library/scala/runtime/VolatileDoubleRef.java +++ b/src/library/scala/runtime/VolatileDoubleRef.java diff --git a/src/library/scala/runtime/VolatileFloatRef.java b/src/library/scala/runtime/VolatileFloatRef.java index 3a81be1146..3a81be1146 100755..100644 --- a/src/library/scala/runtime/VolatileFloatRef.java +++ b/src/library/scala/runtime/VolatileFloatRef.java diff --git a/src/library/scala/runtime/VolatileIntRef.java b/src/library/scala/runtime/VolatileIntRef.java index ae015bc8b1..ae015bc8b1 100755..100644 --- a/src/library/scala/runtime/VolatileIntRef.java +++ b/src/library/scala/runtime/VolatileIntRef.java diff --git a/src/library/scala/runtime/VolatileLongRef.java b/src/library/scala/runtime/VolatileLongRef.java index e596f5aa69..e596f5aa69 100755..100644 --- a/src/library/scala/runtime/VolatileLongRef.java +++ b/src/library/scala/runtime/VolatileLongRef.java diff --git a/src/library/scala/runtime/VolatileObjectRef.java b/src/library/scala/runtime/VolatileObjectRef.java index 6063501ffb..6063501ffb 100755..100644 --- a/src/library/scala/runtime/VolatileObjectRef.java +++ b/src/library/scala/runtime/VolatileObjectRef.java diff --git a/src/library/scala/runtime/VolatileShortRef.java b/src/library/scala/runtime/VolatileShortRef.java index 0a2825941f..0a2825941f 100755..100644 --- a/src/library/scala/runtime/VolatileShortRef.java +++ b/src/library/scala/runtime/VolatileShortRef.java diff --git a/src/library/scala/sys/process/Process.scala b/src/library/scala/sys/process/Process.scala index dcd06c89e9..c40838bb06 100644 --- a/src/library/scala/sys/process/Process.scala +++ b/src/library/scala/sys/process/Process.scala @@ -155,8 +155,8 @@ trait ProcessCreation { * import java.net.URL * import java.io.File * - * val spde = new URL("http://technically.us/spde/About") - * val dispatch = new URL("http://databinder.net/dispatch/About") + * val spde = new URL("http://technically.us/spde.html") + * val dispatch = new URL("http://dispatch.databinder.net/Dispatch.html") * val build = new File("project/build.properties") * cat(spde, dispatch, build) #| "grep -i scala" ! * }}} diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala index 367488f116..d4a5e2f0e8 100644 --- a/src/library/scala/util/Properties.scala +++ b/src/library/scala/util/Properties.scala @@ -1,6 +1,6 @@ /* __ *\ ** ________ ___ / / ___ Scala API ** -** / __/ __// _ | / / / _ | (c) 2006-2013, LAMP/EPFL ** +** / __/ __// _ | / / / _ | (c) 2006-2015, LAMP/EPFL ** ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ ** ** /____/\___/_/ |_/____/_/ | | ** ** |/ ** @@ -105,7 +105,7 @@ private[scala] trait PropertiesTrait { * or "version (unknown)" if it cannot be determined. */ val versionString = "version " + scalaPropOrElse("version.number", "(unknown)") - val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2013, LAMP/EPFL") + val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2015, LAMP/EPFL") /** This is the encoding to use reading in source files, overridden with -encoding. * Note that it uses "prop" i.e. looks in the scala jar, not the system properties. diff --git a/src/manual/scala/man1/Command.scala b/src/manual/scala/man1/Command.scala index 8f811f950e..13e21757c0 100644 --- a/src/manual/scala/man1/Command.scala +++ b/src/manual/scala/man1/Command.scala @@ -42,7 +42,7 @@ trait Command { def authors = Section("AUTHOR", "Written by Martin Odersky and other members of the " & - Link("Scala team", "http://www.scala-lang.org/node/89") & ".") + Link("Scala team", "http://www.scala-lang.org/news/2014/01/22/10-years-of-scala.html") & ".") def copyright = Section("COPYRIGHT", diff --git a/src/reflect/scala/reflect/internal/Chars.scala b/src/reflect/scala/reflect/internal/Chars.scala index 74413fdaba..0f532a4e57 100644 --- a/src/reflect/scala/reflect/internal/Chars.scala +++ b/src/reflect/scala/reflect/internal/Chars.scala @@ -66,7 +66,7 @@ trait Chars { '0' <= c && c <= '9' || 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' /** Can character start an alphanumeric Scala identifier? */ - def isIdentifierStart(c: Char): Boolean = + def isIdentifierStart(c: Char): Boolean = (c == '_') || (c == '$') || Character.isUnicodeIdentifierStart(c) /** Can character form part of an alphanumeric Scala identifier? */ diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 05fdfb51ed..c3b2a8da08 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -503,6 +503,19 @@ trait Symbols extends api.Symbols { self: SymbolTable => case _ => new StubTermSymbol(this, name.toTermName, missingMessage) } + /** Given a field, construct a term symbol that represents the source construct that gave rise the the field */ + def sugaredSymbolOrSelf = { + val getter = getterIn(owner) + if (getter == NoSymbol) { + this + } else { + val result = owner.newValue(getter.name.toTermName, newFlags = getter.flags & ~Flags.METHOD).setPrivateWithin(getter.privateWithin).setInfo(getter.info.resultType) + val setter = setterIn(owner) + if (setter != NoSymbol) result.setFlag(Flags.MUTABLE) + result + } + } + // ----- locking and unlocking ------------------------------------------------------ // True if the symbol is unlocked. diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 375218248e..2ae95c98e5 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -2524,6 +2524,9 @@ trait Types override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def narrow: Type = resultType.narrow + // SI-9475: PolyTypes with dependent method types are still dependent + override def isDependentMethodType = resultType.isDependentMethodType + /** @M: typeDefSig wraps a TypeBounds in a PolyType * to represent a higher-kinded type parameter * wrap lo&hi in polytypes to bind variables diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 15e0f9cc14..277e89d656 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala @@ -243,7 +243,7 @@ abstract class UnPickler { (module map { case (group, art) => s"""\n(NOTE: It looks like the $art module is missing; try adding a dependency on "$group" : "$art". - | See http://docs.scala-lang.org/overviews/core/scala-2.11.html for more information.)""".stripMargin + | See http://docs.scala-lang.org/overviews/ for more information.)""".stripMargin } getOrElse "") } diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala index d6d216863b..28a7fb37a6 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala @@ -343,7 +343,7 @@ private[internal] trait TypeMaps { object rawToExistentialInJava extends TypeMap { def apply(tp: Type): Type = tp match { // any symbol that occurs in a java sig, not just java symbols - // see http://lampsvn.epfl.ch/trac/scala/ticket/2454#comment:14 + // see https://issues.scala-lang.org/browse/SI-2454?focusedCommentId=46618 case TypeRef(pre, sym, List()) if !sym.typeParams.isEmpty => val eparams = typeParamsToExistentials(sym, sym.typeParams) existentialAbstraction(eparams, TypeRef(pre, sym, eparams map (_.tpe))) diff --git a/src/reflect/scala/reflect/macros/blackbox/Context.scala b/src/reflect/scala/reflect/macros/blackbox/Context.scala index 2f9c512efa..ce28b5911e 100644 --- a/src/reflect/scala/reflect/macros/blackbox/Context.scala +++ b/src/reflect/scala/reflect/macros/blackbox/Context.scala @@ -29,7 +29,7 @@ package blackbox * which means that its expansion will be upcast to its return type, enforcing faithfullness of that macro to its * type signature. Whitebox macros, i.e. the ones defined with `whitebox.Context`, aren't bound by this restriction, * which enables a number of important use cases, but they are also going to enjoy less support than blackbox macros, - * so choose wisely. See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] for more information. + * so choose wisely. See the [[http://docs.scala-lang.org/overviews/macros/overview.html Macros Guide]] for more information. * * @see `scala.reflect.macros.whitebox.Context` */ diff --git a/src/reflect/scala/reflect/macros/package.scala b/src/reflect/scala/reflect/macros/package.scala index cc7111d794..b63d419d61 100644 --- a/src/reflect/scala/reflect/macros/package.scala +++ b/src/reflect/scala/reflect/macros/package.scala @@ -10,14 +10,14 @@ package reflect * Within these functions the programmer has access to compiler APIs. * For example, it is possible to generate, analyze and typecheck code. * - * See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] on how to get started with Scala macros. + * See the [[http://docs.scala-lang.org/overviews/macros/overview.html Macros Guide]] on how to get started with Scala macros. */ package object macros { /** The Scala macros context. * * In Scala 2.11, macros that were once the one are split into blackbox and whitebox macros, * with the former being better supported and the latter being more powerful. You can read about - * the details of the split and the associated trade-offs in the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]]. + * the details of the split and the associated trade-offs in the [[http://docs.scala-lang.org/overviews/macros/overview.html Macros Guide]]. * * `scala.reflect.macros.Context` follows this tendency and turns into `scala.reflect.macros.blackbox.Context` * and `scala.reflect.macros.whitebox.Context`. The original `Context` is left in place for compatibility reasons, diff --git a/src/reflect/scala/reflect/macros/whitebox/Context.scala b/src/reflect/scala/reflect/macros/whitebox/Context.scala index bd48df46cc..272991cba9 100644 --- a/src/reflect/scala/reflect/macros/whitebox/Context.scala +++ b/src/reflect/scala/reflect/macros/whitebox/Context.scala @@ -29,7 +29,7 @@ package whitebox * gaining the ability to refine the type of its expansion beyond its official return type, which enables a number of important use cases. * Blackbox macros, i.e. the ones defined with `blackbox.Context`, can't do that, so they are less powerful. * However blackbox macros are also going to enjoy better support than whitebox macros, so choose wisely. - * See the [[http://docs.scala-lang.org/overviews/macros.html Macros Guide]] for more information. + * See the [[http://docs.scala-lang.org/overviews/macros/overview.html Macros Guide]] for more information. * * @see `scala.reflect.macros.blackbox.Context` */ diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala index c18a9809a0..89e849429d 100644 --- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala +++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineDelimiter.scala @@ -11,7 +11,7 @@ import _root_.jline.console.completer.ArgumentCompleter.{ ArgumentDelimiter, Arg // implements a jline interface class JLineDelimiter extends ArgumentDelimiter { - def toJLine(args: List[String], cursor: Int) = args match { + def toJLine(args: List[String], cursor: Int): ArgumentList = args match { case Nil => new ArgumentList(new Array[String](0), 0, 0, cursor) case xs => new ArgumentList(xs.toArray, xs.size - 1, xs.last.length, cursor) } diff --git a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala index f0fce13fe8..b5db4c2098 100644 --- a/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala +++ b/src/repl-jline/scala/tools/nsc/interpreter/jline/JLineReader.scala @@ -10,12 +10,13 @@ package scala.tools.nsc.interpreter.jline import java.util.{Collection => JCollection, List => JList} import _root_.jline.{console => jconsole} -import jconsole.completer.{Completer, ArgumentCompleter} +import jline.console.ConsoleReader +import jline.console.completer.{CompletionHandler, Completer, ArgumentCompleter} import jconsole.history.{History => JHistory} import scala.tools.nsc.interpreter -import scala.tools.nsc.interpreter.Completion +import scala.tools.nsc.interpreter.{Completion, JLineCompletion, NoCompletion} import scala.tools.nsc.interpreter.Completion.Candidates import scala.tools.nsc.interpreter.session.History @@ -121,23 +122,48 @@ private class JLineConsoleReader extends jconsole.ConsoleReader with interpreter def initCompletion(completion: Completion): Unit = { this setBellEnabled false - if (completion ne interpreter.NoCompletion) { - val jlineCompleter = new ArgumentCompleter(new JLineDelimiter, - new Completer { - val tc = completion.completer() - def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { - val buf = if (_buf == null) "" else _buf - val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor) - newCandidates foreach (candidates add _) - newCursor - } - } - ) - - jlineCompleter setStrict false - - this addCompleter jlineCompleter - this setAutoprintThreshold 400 // max completion candidates without warning + // adapt the JLine completion interface + def completer = + new Completer { + val tc = completion.completer() + def complete(_buf: String, cursor: Int, candidates: JList[CharSequence]): Int = { + val buf = if (_buf == null) "" else _buf + val Candidates(newCursor, newCandidates) = tc.complete(buf, cursor) + newCandidates foreach (candidates add _) + newCursor + } + } + + // a last bit of nastiness: parsing help depending on the flavor of completer (fixme) + completion match { + case _: JLineCompletion => + val jlineCompleter = new ArgumentCompleter(new JLineDelimiter, completer) + jlineCompleter setStrict false + this addCompleter jlineCompleter + case NoCompletion => () + case _ => this addCompleter completer } + + // This is a workaround for https://github.com/jline/jline2/issues/208 + // and should not be necessary once we upgrade to JLine 2.13.1 + /// + // Test by: + // scala> {" ".char}<LEFT><TAB> + // + // And checking we don't get an extra } on the line. + /// + val handler = getCompletionHandler + setCompletionHandler(new CompletionHandler { + override def complete(consoleReader: ConsoleReader, list: JList[CharSequence], i: Int): Boolean = { + try { + handler.complete(consoleReader, list, i) + } finally if (getCursorBuffer.cursor != getCursorBuffer.length()) { + print(" ") + getCursorBuffer.write(' ') + backspace() + } + } + }) + setAutoprintThreshold(400) // max completion candidates without warning } } diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 612bdd98c9..6721ff6dd6 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -1,8 +1,7 @@ /* NSC -- new Scala compiler - * Copyright 2005-2013 LAMP/EPFL + * Copyright 2005-2015 LAMP/EPFL * @author Alexander Spoon */ - package scala package tools.nsc package interpreter @@ -24,9 +23,9 @@ import io.AbstractFile import scala.collection.generic.Clearable import scala.concurrent.{ ExecutionContext, Await, Future, future } import ExecutionContext.Implicits._ -import java.io.{ BufferedReader, FileReader } +import java.io.{ BufferedReader, FileReader, StringReader } -import scala.util.{Try, Success, Failure} +import scala.util.{ Try, Success, Failure } /** The Scala interactive shell. It provides a read-eval-print loop * around the Interpreter class. @@ -657,12 +656,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) unleashAndSetPhase() asyncEcho(isDuringInit, power.banner) } - private def unleashAndSetPhase() { - if (isReplPower) { - power.unleash() - // Set the phase to "typer" - intp beSilentDuring phaseCommand("typer") - } + private def unleashAndSetPhase() = if (isReplPower) { + power.unleash() + intp beSilentDuring phaseCommand("typer") // Set the phase to "typer" } def asyncEcho(async: Boolean, msg: => String) { @@ -750,28 +746,9 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) result } - private object paste extends Pasted { - import scala.util.matching.Regex.quote - val ContinuePrompt = replProps.continuePrompt - val ContinueString = replProps.continueText // " | " - val PromptString = prompt.lines.toList.last - val anyPrompt = s"""\\s*(?:${quote(PromptString.trim)}|${quote(AltPromptString.trim)})\\s*""".r - - def isPrompted(line: String) = matchesPrompt(line) - def isPromptOnly(line: String) = line match { case anyPrompt() => true ; case _ => false } - - def interpret(line: String): Unit = { - echo(line.trim) - intp interpret line - echo("") - } - - def transcript(start: String) = { - echo("\n// Detected repl transcript paste: ctrl-D to finish.\n") - apply(Iterator(start) ++ readWhile(!isPromptOnly(_))) - } - - def unapply(line: String): Boolean = isPrompted(line) + private object paste extends Pasted(prompt) { + def interpret(line: String) = intp interpret line + def echo(message: String) = ILoop.this echo message } private object invocation { @@ -786,30 +763,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) * read, go ahead and interpret it. Return the full string * to be recorded for replay, if any. */ - def interpretStartingWith(code: String): Option[String] = { + final def interpretStartingWith(code: String): Option[String] = { // signal completion non-completion input has been received in.completion.resetVerbosity() - def reallyInterpret = intp.interpret(code) match { - case IR.Error => None - case IR.Success => Some(code) - case IR.Incomplete if in.interactive && code.endsWith("\n\n") => - echo("You typed two blank lines. Starting a new command.") - None - case IR.Incomplete => - in.readLine(paste.ContinuePrompt) match { - case null => - // we know compilation is going to fail since we're at EOF and the - // parser thinks the input is still incomplete, but since this is - // a file being read non-interactively we want to fail. So we send - // it straight to the compiler for the nice error message. - intp.compileString(code) - None - - case line => interpretStartingWith(code + "\n" + line) - } - } - /* Here we place ourselves between the user and the interpreter and examine * the input they are ostensibly submitting. We intervene in several cases: * @@ -822,9 +779,33 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) code match { case "" => None case lineComment() => None // line comment, do nothing - case paste() if !paste.running => paste.transcript(code) ; None + case paste() if !paste.running => paste.transcript(Iterator(code) ++ readWhile(!paste.isPromptOnly(_))) match { + case Some(s) => interpretStartingWith(s) + case _ => None + } case invocation() if intp.mostRecentVar != "" => interpretStartingWith(intp.mostRecentVar + code) - case _ => reallyInterpret + case _ => intp.interpret(code) match { + case IR.Error => None + case IR.Success => Some(code) + case IR.Incomplete if in.interactive && code.endsWith("\n\n") => + echo("You typed two blank lines. Starting a new command.") + None + case IR.Incomplete => + val saved = intp.partialInput + intp.partialInput = code + "\n" + try { + in.readLine(paste.ContinuePrompt) match { + case null => + // we know compilation is going to fail since we're at EOF and the + // parser thinks the input is still incomplete, but since this is + // a file being read non-interactively we want to fail. So we send + // it straight to the compiler for the nice error message. + intp.compileString(code) + None + case line => interpretStartingWith(s"$code\n$line") + } + } finally intp.partialInput = saved + } } } @@ -840,9 +821,10 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) case _ => } - /** Tries to create a JLineReader, falling back to SimpleReader: - * unless settings or properties are such that it should start - * with SimpleReader. + /** Tries to create a JLineReader, falling back to SimpleReader, + * unless settings or properties are such that it should start with SimpleReader. + * The constructor of the InteractiveReader must take a Completion strategy, + * supplied as a `() => Completion`; the Completion object provides a concrete Completer. */ def chooseReader(settings: Settings): InteractiveReader = { if (settings.Xnojline || Properties.isEmacsShell) SimpleReader() @@ -850,20 +832,25 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) type Completer = () => Completion type ReaderMaker = Completer => InteractiveReader - def instantiate(className: String): ReaderMaker = completer => { - if (settings.debug) Console.println(s"Trying to instantiate a InteractiveReader from $className") + def instantiater(className: String): ReaderMaker = completer => { + if (settings.debug) Console.println(s"Trying to instantiate an InteractiveReader from $className") Class.forName(className).getConstructor(classOf[Completer]). newInstance(completer). asInstanceOf[InteractiveReader] } - def mkReader(maker: ReaderMaker) = - if (settings.noCompletion) maker(() => NoCompletion) - else maker(() => new JLineCompletion(intp)) // JLineCompletion is a misnomer -- it's not tied to jline + def mkReader(maker: ReaderMaker) = maker { () => + settings.completion.value match { + case _ if settings.noCompletion => NoCompletion + case "none" => NoCompletion + case "adhoc" => new JLineCompletion(intp) // JLineCompletion is a misnomer; it's not tied to jline + case "pc" | _ => new PresentationCompilerCompleter(intp) + } + } def internalClass(kind: String) = s"scala.tools.nsc.interpreter.$kind.InteractiveReader" val readerClasses = sys.props.get("scala.repl.reader").toStream ++ Stream(internalClass("jline"), internalClass("jline_embedded")) - val readers = readerClasses map (cls => Try { mkReader(instantiate(cls)) }) + val readers = readerClasses map (cls => Try { mkReader(instantiater(cls)) }) val reader = (readers collect { case Success(reader) => reader } headOption) getOrElse SimpleReader() diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index 3b54f5274e..ef6ab4063a 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -15,10 +15,13 @@ import scala.concurrent.{ Future, ExecutionContext } import scala.reflect.runtime.{ universe => ru } import scala.reflect.{ ClassTag, classTag } import scala.reflect.internal.util.{ BatchSourceFile, SourceFile } +import scala.tools.nsc.interactive +import scala.tools.nsc.reporters.StoreReporter +import scala.tools.nsc.util.ClassPath.DefaultJavaContext import scala.tools.util.PathResolverFactory import scala.tools.nsc.io.AbstractFile import scala.tools.nsc.typechecker.{ TypeStrings, StructuredTypeStrings } -import scala.tools.nsc.util.{ ScalaClassLoader, stringFromReader, stringFromWriter, StackTraceOps, ClassPath, MergedClassPath } +import scala.tools.nsc.util._ import ScalaClassLoader.URLClassLoader import scala.tools.nsc.util.Exceptional.unwrap import scala.tools.nsc.backend.JavaPlatform @@ -58,7 +61,7 @@ import java.io.File * @author Moez A. Abdel-Gawad * @author Lex Spoon */ -class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Settings, protected val out: JPrintWriter) extends AbstractScriptEngine with Compilable with Imports { +class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Settings, protected val out: JPrintWriter) extends AbstractScriptEngine with Compilable with Imports with PresentationCompilation { imain => setBindings(createBindings, ScriptContext.ENGINE_SCOPE) @@ -77,6 +80,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set private var _isInitialized: Future[Boolean] = null // set up initialization future private var bindExceptions = true // whether to bind the lastException variable private var _executionWrapper = "" // code to be wrapped around all lines + var partialInput: String = "" // code accumulated in multi-line REPL input /** We're going to go to some trouble to initialize the compiler asynchronously. * It's critical that nothing call into it until it's been initialized or we will @@ -446,7 +450,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set /** Build a request from the user. `trees` is `line` after being parsed. */ - private def buildRequest(line: String, trees: List[Tree]): Request = { + private[interpreter] def buildRequest(line: String, trees: List[Tree]): Request = { executingRequest = new Request(line, trees) executingRequest } @@ -465,11 +469,12 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set pos } - private def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = { - val content = line //indentCode(line) - val trees = parse(content) match { - case parse.Incomplete => return Left(IR.Incomplete) - case parse.Error => return Left(IR.Error) + private[interpreter] def requestFromLine(line: String, synthetic: Boolean): Either[IR.Result, Request] = { + val content = line + + val trees: List[global.Tree] = parse(content) match { + case parse.Incomplete(_) => return Left(IR.Incomplete) + case parse.Error(_) => return Left(IR.Error) case parse.Success(trees) => trees } repltrace( @@ -854,7 +859,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set } /** One line of code submitted by the user for interpretation */ - class Request(val line: String, val trees: List[Tree]) { + class Request(val line: String, val trees: List[Tree], generousImports: Boolean = false) { def defines = defHandlers flatMap (_.definedSymbols) def imports = importedSymbols def value = Some(handlers.last) filter (h => h.definesValue) map (h => definedSymbols(h.definesTerm.get)) getOrElse NoSymbol @@ -888,7 +893,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set * append to objectName to access anything bound by request. */ lazy val ComputedImports(headerPreamble, importsPreamble, importsTrailer, accessPath) = - exitingTyper(importsCode(referencedNames.toSet, ObjectSourceCode, definesClass)) + exitingTyper(importsCode(referencedNames.toSet, ObjectSourceCode, definesClass, generousImports)) /** the line of code to compute */ def toCompute = line @@ -915,6 +920,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set |${envLines mkString (" ", ";\n ", ";\n")} |$importsPreamble |${indentCode(toCompute)}""".stripMargin + def preambleLength = preamble.length - toCompute.length - 1 val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this @@ -954,7 +960,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set def postwrap = s"}\nval $iw = new $iw\n" } - private lazy val ObjectSourceCode: Wrapper = + private[interpreter] lazy val ObjectSourceCode: Wrapper = if (isClassBased) new ClassBasedWrapper else new ObjectBasedWrapper private object ResultObjectSourceCode extends IMain.CodeAssembler[MemberHandler] { @@ -1015,6 +1021,7 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set } lazy val resultSymbol = lineRep.resolvePathToSymbol(fullAccessPath) + def applyToResultMember[T](name: Name, f: Symbol => T) = exitingTyper(f(resultSymbol.info.nonPrivateDecl(name))) /* typeOf lookup with encoding */ @@ -1168,20 +1175,22 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set /** Parse a line into and return parsing result (error, incomplete or success with list of trees) */ object parse { - abstract sealed class Result - case object Error extends Result - case object Incomplete extends Result + abstract sealed class Result { def trees: List[Tree] } + case class Error(trees: List[Tree]) extends Result + case class Incomplete(trees: List[Tree]) extends Result case class Success(trees: List[Tree]) extends Result def apply(line: String): Result = debugging(s"""parse("$line")""") { var isIncomplete = false - currentRun.parsing.withIncompleteHandler((_, _) => isIncomplete = true) { + def parse = { reporter.reset() val trees = newUnitParser(line).parseStats() - if (reporter.hasErrors) Error - else if (isIncomplete) Incomplete + if (reporter.hasErrors) Error(trees) + else if (isIncomplete) Incomplete(trees) else Success(trees) } + currentRun.parsing.withIncompleteHandler((_, _) => isIncomplete = true) {parse} + } } @@ -1264,6 +1273,8 @@ class IMain(@BeanProperty val factory: ScriptEngineFactory, initialSettings: Set /** Utility methods for the Interpreter. */ object IMain { import java.util.Arrays.{ asList => asJavaList } + /** Dummy identifier fragement inserted at the cursor before presentation compilation. Needed to support completion of `global.def<TAB>` */ + val DummyCursorFragment = "_CURSOR_" class Factory extends ScriptEngineFactory { @BeanProperty @@ -1361,3 +1372,4 @@ object IMain { def stripImpl(str: String): String = naming.unmangle(str) } } + diff --git a/src/repl/scala/tools/nsc/interpreter/Imports.scala b/src/repl/scala/tools/nsc/interpreter/Imports.scala index 5b231d94b6..5742c1d0d8 100644 --- a/src/repl/scala/tools/nsc/interpreter/Imports.scala +++ b/src/repl/scala/tools/nsc/interpreter/Imports.scala @@ -95,7 +95,8 @@ trait Imports { * last one imported is actually usable. */ case class ComputedImports(header: String, prepend: String, append: String, access: String) - protected def importsCode(wanted: Set[Name], wrapper: Request#Wrapper, definesClass: Boolean): ComputedImports = { + + protected def importsCode(wanted: Set[Name], wrapper: Request#Wrapper, definesClass: Boolean, generousImports: Boolean): ComputedImports = { val header, code, trailingBraces, accessPath = new StringBuilder val currentImps = mutable.HashSet[Name]() var predefEscapes = false // only emit predef import header if name not resolved in history, loosely @@ -116,8 +117,9 @@ trait Imports { def keepHandler(handler: MemberHandler) = handler match { // While defining classes in class based mode - implicits are not needed. case h: ImportHandler if isClassBased && definesClass => h.importedNames.exists(x => wanted.contains(x)) - case _: ImportHandler => true - case x => x.definesImplicit || (x.definedNames exists wanted) + case _: ImportHandler => true + case x if generousImports => x.definesImplicit || (x.definedNames exists (d => wanted.exists(w => d.startsWith(w)))) + case x => x.definesImplicit || (x.definedNames exists wanted) } reqs match { diff --git a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala index d878988e26..e9b0234a4f 100644 --- a/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala +++ b/src/repl/scala/tools/nsc/interpreter/JLineCompletion.scala @@ -9,6 +9,7 @@ package interpreter import Completion._ import scala.collection.mutable.ListBuffer import scala.reflect.internal.util.StringOps.longestCommonPrefix +import scala.tools.nsc.interactive.Global // REPL completor - queries supplied interpreter for valid // completions based on current contents of buffer. @@ -296,7 +297,6 @@ class JLineCompletion(val intp: IMain) extends Completion with CompletionOutput override def complete(buf: String, cursor: Int): Candidates = { verbosity = if (isConsecutiveTabs(buf, cursor)) verbosity + 1 else 0 repldbg(f"%ncomplete($buf, $cursor%d) last = ($lastBuf, $lastCursor%d), verbosity: $verbosity") - // we don't try lower priority completions unless higher ones return no results. def tryCompletion(p: Parsed, completionFunction: Parsed => List[String]): Option[Candidates] = { val winners = completionFunction(p) diff --git a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala index bcba7b6dfd..4e45f6d615 100644 --- a/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala +++ b/src/repl/scala/tools/nsc/interpreter/MemberHandlers.scala @@ -35,7 +35,16 @@ trait MemberHandlers { // XXX this is obviously inadequate but it's going to require some effort // to get right. if (name.toString startsWith "x$") () - else importVars += name + else { + importVars += name + // Needed to import `xxx` during line 2 of: + // scala> val xxx = "" + // scala> def foo: x<TAB> + if (name.endsWith(IMain.DummyCursorFragment)) { + val stripped = name.stripSuffix(IMain.DummyCursorFragment) + importVars += stripped + } + } case _ => super.traverse(ast) } } diff --git a/src/repl/scala/tools/nsc/interpreter/Pasted.scala b/src/repl/scala/tools/nsc/interpreter/Pasted.scala index f8d8c2ddb1..3a7eda1b77 100644 --- a/src/repl/scala/tools/nsc/interpreter/Pasted.scala +++ b/src/repl/scala/tools/nsc/interpreter/Pasted.scala @@ -15,19 +15,23 @@ package interpreter * a transcript should itself be pasteable and should achieve * the same result. */ -abstract class Pasted { - def interpret(line: String): Unit - def ContinueString: String - def PromptString: String - def AltPromptString: String = "scala> " +abstract class Pasted(prompt: String) { + def interpret(line: String): IR.Result + def echo(message: String): Unit - /* `testBoth` cannot be a val, as `Pasted` is inherited by `object paste` in ILoop, - which would cause `val testBoth` to be initialized before `val PromptString` was. + val PromptString = prompt.lines.toList.last + val AltPromptString = "scala> " + val ContinuePrompt = replProps.continuePrompt + val ContinueString = replProps.continueText // " | " + val anyPrompt = { + import scala.util.matching.Regex.quote + s"""\\s*(?:${quote(PromptString.trim)}|${quote(AltPromptString.trim)})\\s*""".r + } + + def isPrompted(line: String) = matchesPrompt(line) + def isPromptOnly(line: String) = line match { case anyPrompt() => true ; case _ => false } - object paste extends Pasted { - val PromptString = prompt.lines.toList.last - */ - private def testBoth = PromptString != AltPromptString + private val testBoth = PromptString != AltPromptString private val spacey = " \t".toSet def matchesPrompt(line: String) = matchesString(line, PromptString) || testBoth && matchesString(line, AltPromptString) @@ -91,13 +95,26 @@ abstract class Pasted { case _ => code } - def run(): Unit = { - println("// Replaying %d commands from transcript.\n" format cmds.size) - cmds foreach { cmd => - print(ActualPromptString) - interpret(cmd) - } + def interpreted(line: String) = { + echo(line.trim) + val res = interpret(line) + if (res != IR.Incomplete) echo("") + res + } + def incompletely(cmd: String) = { + print(ActualPromptString) + interpreted(cmd) == IR.Incomplete } + def run(): Option[String] = { + echo(s"// Replaying ${cmds.size} commands from transcript.\n") + cmds find incompletely + } + } + + // Run transcript and return incomplete line if any. + def transcript(lines: TraversableOnce[String]): Option[String] = { + echo("\n// Detected repl transcript. Paste more, or ctrl-D to finish.\n") + apply(lines) } /** Commands start on lines beginning with "scala>" and each successive @@ -105,9 +122,10 @@ abstract class Pasted { * Everything else is discarded. When the end of the transcript is spotted, * all the commands are replayed. */ - def apply(lines: TraversableOnce[String]) = { + def apply(lines: TraversableOnce[String]): Option[String] = { isRunning = true - try new PasteAnalyzer(lines.toList) run() + try new PasteAnalyzer(lines.toList).run() finally isRunning = false } + def unapply(line: String): Boolean = isPrompted(line) } diff --git a/src/repl/scala/tools/nsc/interpreter/Power.scala b/src/repl/scala/tools/nsc/interpreter/Power.scala index 8d8140b638..a14a60d216 100644 --- a/src/repl/scala/tools/nsc/interpreter/Power.scala +++ b/src/repl/scala/tools/nsc/interpreter/Power.scala @@ -113,10 +113,13 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re } } - private def customBanner = replProps.powerBanner.option flatMap (f => io.File(f).safeSlurp()) + private def customBanner = replProps.powerBanner.option flatMap { + case f if f.getName == "classic" => Some(classic) + case f => io.File(f).safeSlurp() + } private def customInit = replProps.powerInitCode.option flatMap (f => io.File(f).safeSlurp()) - def banner = customBanner getOrElse """ + def classic = """ |** Power User mode enabled - BEEP WHIR GYVE ** |** :phase has been set to 'typer'. ** |** scala.tools.nsc._ has been imported ** @@ -124,28 +127,30 @@ class Power[ReplValsImpl <: ReplVals : ru.TypeTag: ClassTag](val intp: IMain, re |** Try :help, :vals, power.<tab> ** """.stripMargin.trim - private def initImports = List( - "scala.tools.nsc._", - "scala.collection.JavaConverters._", - "intp.global.{ error => _, _ }", - "definitions.{ getClass => _, _ }", - "power.rutil._", - "replImplicits._", - "treedsl.CODE._" - ) - - def init = customInit match { - case Some(x) => x - case _ => initImports.mkString("import ", ", ", "") - } + def banner = customBanner getOrElse """ + |Power mode enabled. :phase is at typer. + |import scala.tools.nsc._, intp.global._, definitions._ + |Try :help or completions for vals._ and power._ + """.stripMargin.trim + + private def initImports = + """scala.tools.nsc._ + |scala.collection.JavaConverters._ + |intp.global.{ error => _, _ } + |definitions.{ getClass => _, _ } + |power.rutil._ + |replImplicits._ + |treedsl.CODE._""".stripMargin.lines + + def init = customInit getOrElse initImports.mkString("import ", ", ", "") - /** Starts up power mode and runs whatever is in init. + /** Quietly starts up power mode and runs whatever is in init. */ def unleash(): Unit = beQuietDuring { // First we create the ReplVals instance and bind it to $r intp.bind("$r", replVals) // Then we import everything from $r. - intp interpret ("import " + intp.originalPath("$r") + "._") + intp interpret s"import ${ intp.originalPath("$r") }._" // And whatever else there is to do. init.lines foreach (intp interpret _) } diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala new file mode 100644 index 0000000000..3a2177a4cb --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilation.scala @@ -0,0 +1,110 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2015 LAMP/EPFL + * @author Martin Odersky + */ +package scala.tools.nsc.interpreter + +import scala.reflect.internal.util.RangePosition +import scala.tools.nsc.backend.JavaPlatform +import scala.tools.nsc.{interactive, Settings} +import scala.tools.nsc.io._ +import scala.tools.nsc.reporters.StoreReporter +import scala.tools.nsc.util.ClassPath.DefaultJavaContext +import scala.tools.nsc.util.{DirectoryClassPath, MergedClassPath} + +trait PresentationCompilation { + self: IMain => + + /** Typecheck a line of REPL input, suitably wrapped with "interpreter wrapper" objects/classes, with the + * presentation compiler. The result of this method gives access to the typechecked tree and to autocompletion + * suggestions. + * + * The caller is responsible for calling [[PresentationCompileResult#cleanup]] to dispose of the compiler instance. + */ + private[scala] def presentationCompile(line: String): Either[IR.Result, PresentationCompileResult] = { + if (global == null) Left(IR.Error) + else { + // special case for: + // + // scala> 1 + // scala> .toInt + // + // and for multi-line input. + val line1 = partialInput + (if (Completion.looksLikeInvocation(line)) { self.mostRecentVar + line } else line) + val compiler = newPresentationCompiler() + val trees = compiler.newUnitParser(line1).parseStats() + val importer = global.mkImporter(compiler) + val request = new Request(line1, trees map (t => importer.importTree(t)), generousImports = true) + val wrappedCode: String = request.ObjectSourceCode(request.handlers) + val unit = compiler.newCompilationUnit(wrappedCode) + import compiler._ + val richUnit = new RichCompilationUnit(unit.source) + unitOfFile(richUnit.source.file) = richUnit + enteringTyper(typeCheck(richUnit)) + val result = PresentationCompileResult(compiler)(richUnit, request.ObjectSourceCode.preambleLength + line1.length - line.length) + Right(result) + } + } + + /** Create an instance of the presentation compiler with a classpath comprising the REPL's configured classpath + * and the classes output by previously compiled REPL lines. + * + * You may directly interact with this compiler from any thread, although you must not access it concurrently + * from multiple threads. + * + * You may downcast the `reporter` to `StoreReporter` to access type errors. + */ + def newPresentationCompiler(): interactive.Global = { + val replOutClasspath: DirectoryClassPath = new DirectoryClassPath(replOutput.dir, DefaultJavaContext) + val mergedClasspath = new MergedClassPath[AbstractFile](replOutClasspath :: global.platform.classPath :: Nil, DefaultJavaContext) + def copySettings: Settings = { + val s = new Settings(_ => () /* ignores "bad option -nc" errors, etc */) + s.processArguments(global.settings.recreateArgs, processAll = false) + s.YpresentationAnyThread.value = true + s + } + val storeReporter: StoreReporter = new StoreReporter + val interactiveGlobal = new interactive.Global(copySettings, storeReporter) { self => + override lazy val platform: ThisPlatform = new JavaPlatform { + val global: self.type = self + + override def classPath: PlatformClassPath = mergedClasspath + } + } + new interactiveGlobal.TyperRun() + interactiveGlobal + } + + abstract class PresentationCompileResult { + val compiler: scala.tools.nsc.interactive.Global + def unit: compiler.RichCompilationUnit + /** The length of synthetic code the precedes the user writtn code */ + def preambleLength: Int + def cleanup(): Unit = { + compiler.askShutdown() + } + import compiler.CompletionResult + + def completionsAt(cursor: Int): CompletionResult = { + val pos = unit.source.position(preambleLength + cursor) + compiler.completionsAt(pos) + } + def typedTreeAt(code: String, selectionStart: Int, selectionEnd: Int): compiler.Tree = { + val start = selectionStart + preambleLength + val end = selectionEnd + preambleLength + val pos = new RangePosition(unit.source, start, start, end) + compiler.typedTreeAt(pos) + } + } + + object PresentationCompileResult { + def apply(compiler0: interactive.Global)(unit0: compiler0.RichCompilationUnit, preambleLength0: Int) = new PresentationCompileResult { + + override val compiler = compiler0 + + override def unit = unit0.asInstanceOf[compiler.RichCompilationUnit] + + override def preambleLength = preambleLength0 + } + } +} diff --git a/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala b/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala new file mode 100644 index 0000000000..0fb3236966 --- /dev/null +++ b/src/repl/scala/tools/nsc/interpreter/PresentationCompilerCompleter.scala @@ -0,0 +1,139 @@ +/* NSC -- new Scala compiler + * Copyright 2005-2013 LAMP/EPFL + * @author Martin Odersky + */ +package scala.tools.nsc.interpreter + +import scala.reflect.internal.Flags +import scala.reflect.internal.util.StringOps +import scala.tools.nsc.interpreter.Completion.{ScalaCompleter, Candidates} +import scala.util.control.NonFatal + +class PresentationCompilerCompleter(intp: IMain) extends Completion with ScalaCompleter { + import PresentationCompilerCompleter._ + import intp.{PresentationCompileResult => Result} + + private type Handler = Result => Candidates + + private var lastRequest = NoRequest + private var tabCount = 0 + private var lastCommonPrefixCompletion: Option[String] = None + + def resetVerbosity(): Unit = { tabCount = 0 ; lastRequest = NoRequest } + def completer(): ScalaCompleter = this + + // A convenience for testing + def complete(before: String, after: String = ""): Candidates = complete(before + after, before.length) + override def complete(buf: String, cursor: Int): Candidates = { + val request = Request(buf, cursor) + if (request == lastRequest) + tabCount += 1 + else { + tabCount = 0 + lastRequest = request + } + + // secret handshakes + val slashPrint = """.*// *print *""".r + val slashTypeAt = """.*// *typeAt *(\d+) *(\d+) *""".r + val Cursor = IMain.DummyCursorFragment + + def print(result: Result) = { + val offset = result.preambleLength + val pos1 = result.unit.source.position(offset).withEnd(offset + buf.length) + import result.compiler._ + val tree = new Locator(pos1) locateIn result.unit.body match { + case Template(_, _, constructor :: (rest :+ last)) => if (rest.isEmpty) last else Block(rest, last) + case t => t + } + val printed = showCode(tree) + " // : " + tree.tpe.safeToString + Candidates(cursor, "" :: printed :: Nil) + } + def typeAt(result: Result, start: Int, end: Int) = { + val tpString = result.compiler.exitingTyper(result.typedTreeAt(buf, start, end).tpe.toString) + Candidates(cursor, "" :: tpString :: Nil) + } + def candidates(result: Result): Candidates = { + import result.compiler._ + import CompletionResult._ + def defStringCandidates(matching: List[Member], name: Name): Candidates = { + val defStrings = for { + member <- matching + if member.symNameDropLocal == name + sym <- member.sym.alternatives + sugared = sym.sugaredSymbolOrSelf + } yield { + val tp = member.prefix memberType sym + sugared.defStringSeenAs(tp) + } + Candidates(cursor, "" :: defStrings.distinct) + } + val found = result.completionsAt(cursor) match { + case NoResults => Completion.NoCandidates + case r => + def shouldHide(m: Member): Boolean = { + val isUniversal = definitions.isUniversalMember(m.sym) + def viaUniversalExtensionMethod = m match { + case t: TypeMember if t.implicitlyAdded && t.viaView.info.params.head.info.bounds.isEmptyBounds => true + case _ => false + } + ( + isUniversal && nme.isReplWrapperName(m.prefix.typeSymbol.name) + || isUniversal && tabCount == 0 && r.name.isEmpty + || viaUniversalExtensionMethod && tabCount == 0 && r.name.isEmpty + ) + } + + val matching = r.matchingResults().filterNot(shouldHide) + val tabAfterCommonPrefixCompletion = lastCommonPrefixCompletion.contains(buf.substring(0, cursor)) && matching.exists(_.symNameDropLocal == r.name) + val doubleTab = tabCount > 0 && matching.forall(_.symNameDropLocal == r.name) + if (tabAfterCommonPrefixCompletion || doubleTab) defStringCandidates(matching, r.name) + else if (matching.isEmpty) { + // Lenient matching based on camel case and on eliding JavaBean "get" / "is" boilerplate + val camelMatches: List[Member] = r.matchingResults(CompletionResult.camelMatch(_)).filterNot(shouldHide) + val memberCompletions = camelMatches.map(_.symNameDropLocal.decoded).distinct.sorted + def allowCompletion = ( + (memberCompletions.size == 1) + || CompletionResult.camelMatch(r.name)(r.name.newName(StringOps.longestCommonPrefix(memberCompletions))) + ) + if (memberCompletions.isEmpty) Completion.NoCandidates + else if (allowCompletion) Candidates(cursor - r.positionDelta, memberCompletions) + else Candidates(cursor, "" :: memberCompletions) + } else if (matching.nonEmpty && matching.forall(_.symNameDropLocal == r.name)) + Completion.NoCandidates // don't offer completion if the only option has been fully typed already + else { + // regular completion + val memberCompletions: List[String] = matching.map(_.symNameDropLocal.decoded).distinct.sorted + Candidates(cursor - r.positionDelta, memberCompletions) + } + } + lastCommonPrefixCompletion = + if (found != Completion.NoCandidates && buf.length >= found.cursor) + Some(buf.substring(0, found.cursor) + StringOps.longestCommonPrefix(found.candidates)) + else + None + found + } + val buf1 = buf.patch(cursor, Cursor, 0) + try { + intp.presentationCompile(buf1) match { + case Left(_) => Completion.NoCandidates + case Right(result) => try { + buf match { + case slashPrint() if cursor == buf.length => print(result) + case slashTypeAt(start, end) if cursor == buf.length => typeAt(result, start.toInt, end.toInt) + case _ => candidates(result) + } + } finally result.cleanup() + } + } catch { + case NonFatal(e) => + if (isReplDebug) e.printStackTrace() + Completion.NoCandidates + } + } +} +object PresentationCompilerCompleter { + private case class Request(line: String, cursor: Int) + private val NoRequest = Request("", -1) +} diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala index fb4ed34571..fb4ed34571 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/base/LinkTo.scala b/src/scaladoc/scala/tools/nsc/doc/base/LinkTo.scala index 3d80f9da52..3d80f9da52 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/LinkTo.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/LinkTo.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/base/MemberLookupBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/MemberLookupBase.scala index 9de6ec4ab9..9de6ec4ab9 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/MemberLookupBase.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/MemberLookupBase.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala index ac5fec80b3..ac5fec80b3 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala +++ b/src/scaladoc/scala/tools/nsc/doc/base/comment/Body.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala index f257153bd7..f257153bd7 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala index 84ee82f994..84ee82f994 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala +++ b/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js index 3d9cf8d465..cf81f7fdf5 100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js @@ -412,7 +412,17 @@ function textFilter() { var query = $("#textfilter input").attr("value") || ''; var queryRegExp = compilePattern(query); - if ((typeof textFilter.lastQuery === "undefined") || (textFilter.lastQuery !== query)) { + // if we are filtering on types, then we have to display types + // ("display packages only" is not possible when filtering) + if (query !== "") { + kindFilter("all"); + } + + // Three things trigger a reload of the left pane list: + // typeof textFilter.lastQuery === "undefined" <-- first load, there is nothing yet in the left pane + // textFilter.lastQuery !== query <-- the filter text has changed + // focusFilterState != null <-- a package has been "focused" + if ((typeof textFilter.lastQuery === "undefined") || (textFilter.lastQuery !== query) || (focusFilterState != null)) { textFilter.lastQuery = query; diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/jquery-ui.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/jquery-ui.js index faab0cf1a3..faab0cf1a3 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/jquery-ui.js +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/jquery-ui.js diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/ref-index.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/ref-index.css index 7d64b9c5c5..7d64b9c5c5 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/ref-index.css +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/ref-index.css diff --git a/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala index ad53dc6bfa..ad53dc6bfa 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/IndexModelFactory.scala diff --git a/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala index c1228e8735..c1228e8735 100755..100644 --- a/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala +++ b/src/scaladoc/scala/tools/nsc/doc/model/TreeFactory.scala |