diff --git a/src/main/scala/com/potenciasoftware/rebel/BaseRepl.scala b/src/main/scala/com/potenciasoftware/rebel/BaseRepl.scala index 60243ae..7c71b03 100644 --- a/src/main/scala/com/potenciasoftware/rebel/BaseRepl.scala +++ b/src/main/scala/com/potenciasoftware/rebel/BaseRepl.scala @@ -1,8 +1,12 @@ package com.potenciasoftware.rebel +import scala.reflect.ClassTag +import scala.reflect.runtime.universe.TypeTag import scala.tools.nsc.Settings +import scala.tools.nsc.interpreter.Repl import scala.tools.nsc.interpreter.shell.ILoop import scala.tools.nsc.interpreter.shell.ShellConfig +import scala.tools.nsc.typechecker.TypeStrings import BaseRepl._ @@ -36,13 +40,26 @@ class BaseRepl { /** Override to provide banner text to display at startup. */ protected val banner: String = WelcomePlaceholder - protected def config: ShellConfig = ShellConfig(settings) + /** + * Override to provide bound values. + * These will be available from within the REPL. + */ + protected def boundValues: Seq[Parameter] = Seq() + + private lazy val repl: ILoop = new ILoop(ShellConfig(settings)) { - protected def repl: ILoop = new ILoop(config) { val _banner = Option(banner).getOrElse(WelcomePlaceholder) override def welcome: String = { _banner.replaceAll(WelcomePlaceholder, super.welcome) } + + override def createInterpreter(interpreterSettings: Settings): Unit = { + super.createInterpreter(interpreterSettings) + intp.beQuietDuring { + for (param <- boundValues) + param.bindTo(intp) + } + } } def run(): Unit = { @@ -52,4 +69,19 @@ class BaseRepl { object BaseRepl { private val WelcomePlaceholder = "%%%%welcome%%%%" + + class Parameter private ( + name: String, + `type`: String, + value: Any, + modifiers: List[String] + ) { + private[BaseRepl] def bindTo(intp: Repl): Unit = + intp.bind(name, `type`, value, modifiers) + } + + object Parameter { + def apply[A: TypeTag: ClassTag](name: String, value: A, modifiers: String*) = + new Parameter(name, TypeStrings.fromTag[A], value, modifiers.toList) + } } diff --git a/src/test/scala/com/potenciasoftware/rebel/BaseReplTest.scala b/src/test/scala/com/potenciasoftware/rebel/BaseReplTest.scala index 582feed..d6e5f9a 100644 --- a/src/test/scala/com/potenciasoftware/rebel/BaseReplTest.scala +++ b/src/test/scala/com/potenciasoftware/rebel/BaseReplTest.scala @@ -7,15 +7,16 @@ import scala.tools.nsc.Settings import scala.tools.nsc.interpreter.shell.ILoop import scala.tools.nsc.interpreter.shell.ShellConfig -import TestUtils._ +import BaseRepl.Parameter import BaseReplTest._ +import TestUtils._ class BaseReplTest extends AnyFlatSpec with Matchers { def basic() = new TestRepl() "BaseRepl" should "behave like the normal ILoop" in { - replTest[BaseReplTest]("basic", Seq("42 + 42")) + replTest[BaseReplTest]("basic", "42 + 42") .out.asBlock shouldBe """ |scala> val res0: Int = 84 | @@ -23,7 +24,7 @@ class BaseReplTest extends AnyFlatSpec with Matchers { } it should "have the default compiler options" in { - replTest[BaseReplTest]("basic", Seq("def test(): Int = 1", "test")).out(3) shouldBe + replTest[BaseReplTest]("basic", "def test(): Int = 1", "test").out(3) shouldBe "scala> warning: 1 deprecation (since 2.13.3); for details, " + "enable `:setting -deprecation` or `:replay -deprecation`" } @@ -37,7 +38,7 @@ class BaseReplTest extends AnyFlatSpec with Matchers { } it should "set custom compiler options" in { - replTest[BaseReplTest]("customCompilerOptions", Seq("def test(): Int = 1", "test")) + replTest[BaseReplTest]("customCompilerOptions", "def test(): Int = 1", "test") .out.drop(2).take(3).asBlock shouldBe """ |scala> val res0: Int = 1 @@ -47,18 +48,31 @@ class BaseReplTest extends AnyFlatSpec with Matchers { def defaultBanner() = new BaseRepl() it should "display the standard ILoop banner by default" in { - replTest[BaseReplTest]("defaultBanner", Seq()) + replTest[BaseReplTest]("defaultBanner") .out.takeWhile(_ != "scala> ").asBlock shouldBe s"${new ILoop(ShellConfig(new Settings)).welcome}\n" } + def customBanner() = new BaseRepl() { + override protected val banner: String = "Custom Banner" + } + it should "display a custom banner" in { - replTest[BaseReplTest]("customBanner", Seq()) + replTest[BaseReplTest]("customBanner") .out.takeWhile(_ != "scala> ").asBlock shouldBe "Custom Banner\n" } - def customBanner() = new BaseRepl() { - override protected val banner: String = "Custom Banner" + def boundAnswer() = new TestRepl { + + private val Answer: Int = 42 + + override protected def boundValues: Seq[Parameter] = + Seq(Parameter("Answer", Answer)) + } + + it should "bind custom variables" in { + replTest[BaseReplTest]("boundAnswer", "println(Answer)") + .out(1) shouldBe "scala> println(Answer)42" } } diff --git a/src/test/scala/com/potenciasoftware/rebel/TestUtils.scala b/src/test/scala/com/potenciasoftware/rebel/TestUtils.scala index 866b4be..7ec44d0 100644 --- a/src/test/scala/com/potenciasoftware/rebel/TestUtils.scala +++ b/src/test/scala/com/potenciasoftware/rebel/TestUtils.scala @@ -83,7 +83,7 @@ object TestUtils { * (Note: including a [[":q"]] line at the end of the input * is not necessary.) */ - def replTest[C: ClassTag](methodName: String, inputLines: Iterable[String]): RunResults = { + def replTest[C: ClassTag](methodName: String, inputLines: String*): RunResults = { import scala.sys.process._ val in = new InputLinesStream(inputLines ++ Seq(":q"))