summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala71
-rw-r--r--src/partest/scala/tools/partest/ReplTest.scala13
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ILoop.scala64
-rw-r--r--test/files/run/t4594-repl-settings.scala26
4 files changed, 132 insertions, 42 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index b5cc89c0c8..0536be92cf 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -9,8 +9,9 @@ package nsc
package settings
import io.{ AbstractFile, Jar, Path, PlainFile, VirtualDirectory }
-import scala.reflect.internal.util.StringOps
+import scala.collection.generic.Clearable
import scala.io.Source
+import scala.reflect.internal.util.StringOps
import scala.reflect.{ ClassTag, classTag }
/** A mutable Settings object.
@@ -542,7 +543,7 @@ class MutableSettings(val errorFn: String => Unit)
name: String,
val arg: String,
descr: String)
- extends Setting(name, descr) {
+ extends Setting(name, descr) with Clearable {
type T = List[String]
protected var v: T = Nil
def appendToValue(str: String) { value ++= List(str) }
@@ -555,6 +556,7 @@ class MutableSettings(val errorFn: String => Unit)
}
override def tryToSetColon(args: List[String]) = tryToSet(args)
override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList) // used from ide
+ def clear(): Unit = (v = Nil)
def unparse: List[String] = value map (name + ":" + _)
withHelpSyntax(name + ":<" + arg + ">")
@@ -608,44 +610,49 @@ class MutableSettings(val errorFn: String => Unit)
name: String,
descr: String,
default: String
- ) extends Setting(name, mkPhasesHelp(descr, default)) {
+ ) extends Setting(name, mkPhasesHelp(descr, default)) with Clearable {
private[nsc] def this(name: String, descr: String) = this(name, descr, "")
type T = List[String]
- protected var v: T = Nil
- override def value = if (v contains "all") List("all") else super.value
- private lazy val (numericValues, stringValues) =
- value filterNot (_ == "" ) partition (_ forall (ch => ch.isDigit || ch == '-'))
-
- /** A little ad-hoc parsing. If a string is not the name of a phase, it can also be:
- * a phase id: 5
- * a phase id range: 5-10 (inclusive of both ends)
- * a range with no start: -5 means up to and including 5
- * a range with no end: 10- means 10 until completion.
- */
- private def stringToPhaseIdTest(s: String): Int => Boolean = (s indexOf '-') match {
- case -1 => (_ == s.toInt)
- case 0 => (_ <= s.tail.toInt)
- case idx =>
- if (s.last == '-') (_ >= s.init.toInt)
- else (s splitAt idx) match {
- case (s1, s2) => (id => id >= s1.toInt && id <= s2.tail.toInt)
- }
- }
- private lazy val phaseIdTest: Int => Boolean =
- (numericValues map stringToPhaseIdTest) match {
- case Nil => _ => false
- case fns => fns.reduceLeft((f1, f2) => id => f1(id) || f2(id))
+ private[this] var _v: T = Nil
+ private[this] var _numbs: List[(Int,Int)] = Nil
+ private[this] var _names: T = Nil
+ //protected var v: T = Nil
+ protected def v: T = _v
+ protected def v_=(t: T): Unit = {
+ // throws NumberFormat on bad range (like -5-6)
+ def asRange(s: String): (Int,Int) = (s indexOf '-') match {
+ case -1 => (s.toInt, s.toInt)
+ case 0 => (-1, s.tail.toInt)
+ case i if s.last == '-' => (s.init.toInt, Int.MaxValue)
+ case i => (s.take(i).toInt, s.drop(i+1).toInt)
}
+ val numsAndStrs = t filter (_.nonEmpty) partition (_ forall (ch => ch.isDigit || ch == '-'))
+ _numbs = numsAndStrs._1 map asRange
+ _names = numsAndStrs._2
+ _v = t
+ }
+ override def value = if (v contains "all") List("all") else super.value // i.e., v
+ private def numericValues = _numbs
+ private def stringValues = _names
+ private def phaseIdTest(i: Int): Boolean = numericValues exists (_ match {
+ case (min, max) => min <= i && i <= max
+ })
def tryToSet(args: List[String]) =
if (default == "") errorAndValue("missing phase", None)
- else { tryToSetColon(List(default)) ; Some(args) }
+ else tryToSetColon(List(default)) map (_ => args)
+
+ override def tryToSetColon(args: List[String]) = try {
+ args match {
+ case Nil => if (default == "") errorAndValue("missing phase", None)
+ else tryToSetColon(List(default))
+ case xs => value = (value ++ xs).distinct.sorted ; Some(Nil)
+ }
+ } catch { case _: NumberFormatException => None }
+
+ def clear(): Unit = (v = Nil)
- override def tryToSetColon(args: List[String]) = args match {
- case Nil => if (default == "") errorAndValue("missing phase", None) else tryToSetColon(List(default))
- case xs => value = (value ++ xs).distinct.sorted ; Some(Nil)
- }
// we slightly abuse the usual meaning of "contains" here by returning
// true if our phase list contains "all", regardless of the incoming argument
def contains(phName: String) = doAllPhases || containsName(phName)
diff --git a/src/partest/scala/tools/partest/ReplTest.scala b/src/partest/scala/tools/partest/ReplTest.scala
index edd1f705a4..7381b8af54 100644
--- a/src/partest/scala/tools/partest/ReplTest.scala
+++ b/src/partest/scala/tools/partest/ReplTest.scala
@@ -7,6 +7,7 @@ package scala.tools.partest
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter.ILoop
+import scala.tools.partest.nest.FileUtil
import java.lang.reflect.{ Method => JMethod, Field => JField }
/** A trait for testing repl code. It drops the first line
@@ -29,3 +30,15 @@ abstract class ReplTest extends DirectTest {
}
def show() = eval() foreach println
}
+
+abstract class SessionTest extends ReplTest with FileUtil {
+ def session: String
+ override final def code = expected filter (_.startsWith(prompt)) map (_.drop(prompt.length)) mkString "\n"
+ def expected = session.stripMargin.lines.toList
+ final def prompt = "scala> "
+ override def show() = {
+ val out = eval().toList
+ if (out.size != expected.size) Console println s"Expected ${expected.size} lines, got ${out.size}"
+ if (out != expected) Console print compareContents(expected, out, "expected", "actual")
+ }
+}
diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
index 81d14c48dc..dc5183fdf6 100644
--- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala
@@ -7,22 +7,22 @@ package scala
package tools.nsc
package interpreter
-import Predef.{ println => _, _ }
-import java.io.{ BufferedReader, FileReader }
-import session._
+import scala.language.{ implicitConversions, existentials }
import scala.annotation.tailrec
+import Predef.{ println => _, _ }
+import interpreter.session._
+import StdReplTags._
import scala.util.Properties.{ jdkHome, javaVersion, versionString, javaVmName }
-import util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream }
-import io.{ File, Directory }
-import util.ScalaClassLoader
+import scala.tools.nsc.util.{ ClassPath, Exceptional, stringFromWriter, stringFromStream }
+import scala.reflect.classTag
+import scala.reflect.internal.util.{ BatchSourceFile, ScalaClassLoader }
import ScalaClassLoader._
+import scala.reflect.io.{ File, Directory }
import scala.tools.util._
-import scala.language.{implicitConversions, existentials}
-import scala.reflect.classTag
-import StdReplTags._
+import scala.collection.generic.Clearable
import scala.concurrent.{ ExecutionContext, Await, Future, future }
import ExecutionContext.Implicits._
-import scala.reflect.internal.util.BatchSourceFile
+import java.io.{ BufferedReader, FileReader }
/** The Scala interactive shell. It provides a read-eval-print loop
* around the Interpreter class.
@@ -223,6 +223,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
nullary("replay", "reset execution and replay all previous commands", replay),
nullary("reset", "reset the repl to its initial state, forgetting all session entries", resetCommand),
shCommand,
+ cmd("settings", "[+|-]<options>", "+enable/-disable flags, set compiler options", changeSettings),
nullary("silent", "disable/enable automatic printing of results", verbosity),
cmd("type", "[-v] <expr>", "display the type of an expression without evaluating it", typeCommand),
cmd("kind", "[-v] <expr>", "display the kind of expression's type", kindCommand),
@@ -302,6 +303,49 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
intp.lastWarnings foreach { case (pos, msg) => intp.reporter.warning(pos, msg) }
}
+ private def changeSettings(args: String): Result = {
+ def showSettings() = {
+ for (s <- settings.userSetSettings.toSeq.sorted) echo(s.toString)
+ }
+ def updateSettings() = {
+ // put aside +flag options
+ val (pluses, rest) = (args split "\\s+").toList partition (_.startsWith("+"))
+ val tmps = new Settings
+ val (ok, leftover) = tmps.processArguments(rest, processAll = true)
+ if (!ok) echo("Bad settings request.")
+ else if (leftover.nonEmpty) echo("Unprocessed settings.")
+ else {
+ // boolean flags set-by-user on tmp copy should be off, not on
+ val offs = tmps.userSetSettings filter (_.isInstanceOf[Settings#BooleanSetting])
+ val (minuses, nonbools) = rest partition (arg => offs exists (_ respondsTo arg))
+ // update non-flags
+ settings.processArguments(nonbools, processAll = true)
+ // also snag multi-value options for clearing, e.g. -Ylog: and -language:
+ for {
+ s <- settings.userSetSettings
+ if s.isInstanceOf[Settings#MultiStringSetting] || s.isInstanceOf[Settings#PhasesSetting]
+ if nonbools exists (arg => arg.head == '-' && arg.last == ':' && (s respondsTo arg.init))
+ } s match {
+ case c: Clearable => c.clear()
+ case _ =>
+ }
+ def update(bs: Seq[String], name: String=>String, setter: Settings#Setting=>Unit) = {
+ for (b <- bs)
+ settings.lookupSetting(name(b)) match {
+ case Some(s) =>
+ if (s.isInstanceOf[Settings#BooleanSetting]) setter(s)
+ else echo(s"Not a boolean flag: $b")
+ case _ =>
+ echo(s"Not an option: $b")
+ }
+ }
+ update(minuses, identity, _.tryToSetFromPropertyValue("false")) // turn off
+ update(pluses, "-" + _.drop(1), _.tryToSet(Nil)) // turn on
+ }
+ }
+ if (args.isEmpty) showSettings() else updateSettings()
+ }
+
private def javapCommand(line: String): Result = {
if (javap == null)
":javap unavailable, no tools.jar at %s. Set JDK_HOME.".format(jdkHome)
diff --git a/test/files/run/t4594-repl-settings.scala b/test/files/run/t4594-repl-settings.scala
new file mode 100644
index 0000000000..d2335460e5
--- /dev/null
+++ b/test/files/run/t4594-repl-settings.scala
@@ -0,0 +1,26 @@
+
+import scala.tools.partest.SessionTest
+
+// Detected repl transcript paste: ctrl-D to finish.
+object Test extends SessionTest {
+ def session =
+""" |Type in expressions to have them evaluated.
+ |Type :help for more information.
+ |
+ |scala> @deprecated(message="Please don't do that.", since="Time began.") def depp = "john"
+ |depp: String
+ |
+ |scala> def a = depp
+ |warning: there were 1 deprecation warning(s); re-run with -deprecation for details
+ |a: String
+ |
+ |scala> :settings +deprecation
+ |
+ |scala> def b = depp
+ |<console>:8: warning: method depp is deprecated: Please don't do that.
+ | def b = depp
+ | ^
+ |b: String
+ |
+ |scala> """
+}