diff --git a/modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala b/modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala
index ea0dc10186..42ccbf27ac 100644
--- a/modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala
+++ b/modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala
@@ -4,6 +4,7 @@ import ai.kien.python.Python
import caseapp.*
import caseapp.core.help.HelpFormat
import coursier.cache.FileCache
+import coursier.core.Version
import coursier.error.{FetchError, ResolutionError}
import dependency.*
@@ -20,6 +21,7 @@ import scala.build.errors.{
}
import scala.build.input.Inputs
import scala.build.internal.{Constants, Runner}
+import scala.build.options.ScalacOpt.noDashPrefixes
import scala.build.options.{BuildOptions, JavaOpt, MaybeScalaVersion, Scope}
import scala.cli.commands.publish.ConfigUtil.*
import scala.cli.commands.run.Run.{
@@ -28,14 +30,14 @@ import scala.cli.commands.run.Run.{
pythonPathEnv
}
import scala.cli.commands.run.RunMode
-import scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}
+import scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, ScalacOptions, SharedOptions}
import scala.cli.commands.util.BuildCommandHelpers
import scala.cli.commands.{ScalaCommand, WatchUtil}
import scala.cli.config.{ConfigDb, Keys}
import scala.cli.packaging.Library
import scala.cli.util.ArgHelpers.*
import scala.cli.util.ConfigDbUtils
-import scala.cli.{CurrentParams, ScalaCli}
+import scala.cli.{CurrentParams, ScalaCli, coursierVersion}
import scala.jdk.CollectionConverters.*
import scala.util.Properties
@@ -390,11 +392,24 @@ object Repl extends ScalaCommand[ReplOptions] with BuildCommandHelpers {
replArgs: Seq[String],
extraEnv: Map[String, String] = Map.empty,
extraProps: Map[String, String] = Map.empty
- ): Unit =
- if (dryRun)
- logger.message("Dry run, not running REPL.")
+ ): Unit = {
+ val isAmmonite = replArtifacts.replMainClass.startsWith("ammonite")
+ if replArgs.exists(_.noDashPrefixes == ScalacOptions.replInitScript) then
+ scalaParams.scalaVersion match
+ case _ if isAmmonite =>
+ logger.message(
+ "The '--repl-init-script' option is not supported with Ammonite. Did you mean to use '--ammonite-arg'?"
+ )
+ case s
+ if s.coursierVersion < "3.6.4-RC1".coursierVersion &&
+ s.coursierVersion < "3.6.4".coursierVersion &&
+ s.coursierVersion < "3.6.4-RC1-bin-20250109-a50a1e4-NIGHTLY".coursierVersion =>
+ logger.message(
+ "The '--repl-init-script option' is only supported starting with Scala 3.6.4 and onwards."
+ )
+ case _ => ()
+ if dryRun then logger.message("Dry run, not running REPL.")
else {
- val isAmmonite = replArtifacts.replMainClass.startsWith("ammonite")
val depClassPathArgs: Seq[String] =
if replArtifacts.depsClassPath.nonEmpty && !isAmmonite then
Seq(
@@ -422,6 +437,7 @@ object Repl extends ScalaCommand[ReplOptions] with BuildCommandHelpers {
if (retCode != 0)
value(Left(new ReplError(retCode)))
}
+ }
def defaultArtifacts(): Either[BuildException, ReplArtifacts] = either {
value {
diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalacOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalacOptions.scala
index e3bbd220a8..bb6eb324ad 100644
--- a/modules/cli/src/main/scala/scala/cli/commands/shared/ScalacOptions.scala
+++ b/modules/cli/src/main/scala/scala/cli/commands/shared/ScalacOptions.scala
@@ -48,6 +48,8 @@ object ScalacOptions {
val YScriptRunnerOption = "Yscriptrunner"
private val scalacOptionsPurePrefixes = Set("V", "W", "X", "Y")
private val scalacOptionsPrefixes = Set("P") ++ scalacOptionsPurePrefixes
+ val replInitScript = "repl-init-script"
+ private val replAliasedOptions = Set(replInitScript)
private val scalacAliasedOptions = // these options don't require being passed after -O and accept an arg
Set(
"coverage-exclude-classlikes",
@@ -61,7 +63,7 @@ object ScalacOptions {
"target",
"source",
YScriptRunnerOption
- )
+ ) ++ replAliasedOptions
private val scalacNoArgAliasedOptions = // these options don't require being passed after -O and don't accept an arg
Set(
"experimental",
diff --git a/modules/cli/src/main/scala/scala/cli/package.scala b/modules/cli/src/main/scala/scala/cli/package.scala
new file mode 100644
index 0000000000..96f0dc2a1c
--- /dev/null
+++ b/modules/cli/src/main/scala/scala/cli/package.scala
@@ -0,0 +1,7 @@
+package scala
+
+import coursier.core.Version
+
+package object cli {
+ extension (s: String) def coursierVersion: Version = Version(s)
+}
diff --git a/modules/integration/src/test/scala/scala/cli/integration/ReplTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/ReplTestDefinitions.scala
index 8a435ac724..a2a08e81be 100644
--- a/modules/integration/src/test/scala/scala/cli/integration/ReplTestDefinitions.scala
+++ b/modules/integration/src/test/scala/scala/cli/integration/ReplTestDefinitions.scala
@@ -86,4 +86,26 @@ abstract class ReplTestDefinitions extends ScalaCliSuite with TestScalaVersionAr
expect(res.exitCode == 0)
}
}
+
+ test("--repl-init-script dry run") {
+ TestInputs.empty.fromRoot { root =>
+ val r = os.proc(
+ TestUtil.cli,
+ "repl",
+ extraOptions,
+ "--repl-init-script",
+ "println(\"Hello\")",
+ "--repl-dry-run"
+ )
+ .call(cwd = root, stderr = os.Pipe, check = false)
+ val warningText =
+ "The '--repl-init-script option' is only supported starting with Scala 3.6.4 and onwards."
+ val coursierScalaVersion = actualScalaVersion.coursierVersion
+ val shouldPrintWarning = coursierScalaVersion < "3.6.4".coursierVersion &&
+ coursierScalaVersion < "3.6.4-RC1".coursierVersion &&
+ coursierScalaVersion < "3.6.4-RC1-bin-20250109-a50a1e4-NIGHTLY".coursierVersion
+ if (shouldPrintWarning) expect(r.err.trim().contains(warningText))
+ else expect(!r.err.trim().contains(warningText))
+ }
+ }
}
diff --git a/website/docs/commands/repl.md b/website/docs/commands/repl.md
index 20dce3e824..2601d1808f 100644
--- a/website/docs/commands/repl.md
+++ b/website/docs/commands/repl.md
@@ -80,6 +80,63 @@ scala> :quit
+## Passing REPL options
+It is also possible to manually pass REPL-specific options.
+It can be done in a couple ways:
+- after the `--` separator, as the REPL itself is the launched app, so its options are app arguments
+
+
+
+```bash ignore
+scala repl -S 3.6.4-RC1 -- --repl-init-script 'println("Hello")'
+```
+
+```
+Hello
+Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).
+Type in expressions for evaluation. Or try :help.
+
+scala>
+```
+
+
+
+- with the `-O`, effectively passing them as compiler options:
+
+
+
+```bash ignore
+scala repl -S 3.6.4-RC1 -O --repl-init-script -O 'println("Hello")'
+```
+
+```
+Hello
+Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).
+Type in expressions for evaluation. Or try :help.
+
+scala>
+```
+
+
+
+- directly, as a Scala CLI option (do note that newly added options from an RC version or a snapshot may not be supported this way just yet):
+
+
+
+```bash ignore
+scala repl -S 3.6.4-RC1 --repl-init-script 'println("Hello")'
+```
+
+```
+Hello
+Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).
+Type in expressions for evaluation. Or try :help.
+
+scala>
+```
+
+
+
## Using Toolkit in REPL
It is also possible to start the scala-cli REPL with [toolkit](https://scala-cli.virtuslab.org/docs/guides/introduction/toolkit/) enabled