Skip to content

Commit 60d17b6

Browse files
authored
Enable direct usage of --repl-init-script with Scala REPL >= 3.6.4-RC1 (#3447)
1 parent 9853b0f commit 60d17b6

File tree

5 files changed

+111
-7
lines changed

5 files changed

+111
-7
lines changed

modules/cli/src/main/scala/scala/cli/commands/repl/Repl.scala

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import ai.kien.python.Python
44
import caseapp.*
55
import caseapp.core.help.HelpFormat
66
import coursier.cache.FileCache
7+
import coursier.core.Version
78
import coursier.error.{FetchError, ResolutionError}
89
import dependency.*
910

@@ -20,6 +21,7 @@ import scala.build.errors.{
2021
}
2122
import scala.build.input.Inputs
2223
import scala.build.internal.{Constants, Runner}
24+
import scala.build.options.ScalacOpt.noDashPrefixes
2325
import scala.build.options.{BuildOptions, JavaOpt, MaybeScalaVersion, Scope}
2426
import scala.cli.commands.publish.ConfigUtil.*
2527
import scala.cli.commands.run.Run.{
@@ -28,14 +30,14 @@ import scala.cli.commands.run.Run.{
2830
pythonPathEnv
2931
}
3032
import scala.cli.commands.run.RunMode
31-
import scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, SharedOptions}
33+
import scala.cli.commands.shared.{HelpCommandGroup, HelpGroup, ScalacOptions, SharedOptions}
3234
import scala.cli.commands.util.BuildCommandHelpers
3335
import scala.cli.commands.{ScalaCommand, WatchUtil}
3436
import scala.cli.config.{ConfigDb, Keys}
3537
import scala.cli.packaging.Library
3638
import scala.cli.util.ArgHelpers.*
3739
import scala.cli.util.ConfigDbUtils
38-
import scala.cli.{CurrentParams, ScalaCli}
40+
import scala.cli.{CurrentParams, ScalaCli, coursierVersion}
3941
import scala.jdk.CollectionConverters.*
4042
import scala.util.Properties
4143

@@ -390,11 +392,24 @@ object Repl extends ScalaCommand[ReplOptions] with BuildCommandHelpers {
390392
replArgs: Seq[String],
391393
extraEnv: Map[String, String] = Map.empty,
392394
extraProps: Map[String, String] = Map.empty
393-
): Unit =
394-
if (dryRun)
395-
logger.message("Dry run, not running REPL.")
395+
): Unit = {
396+
val isAmmonite = replArtifacts.replMainClass.startsWith("ammonite")
397+
if replArgs.exists(_.noDashPrefixes == ScalacOptions.replInitScript) then
398+
scalaParams.scalaVersion match
399+
case _ if isAmmonite =>
400+
logger.message(
401+
"The '--repl-init-script' option is not supported with Ammonite. Did you mean to use '--ammonite-arg'?"
402+
)
403+
case s
404+
if s.coursierVersion < "3.6.4-RC1".coursierVersion &&
405+
s.coursierVersion < "3.6.4".coursierVersion &&
406+
s.coursierVersion < "3.6.4-RC1-bin-20250109-a50a1e4-NIGHTLY".coursierVersion =>
407+
logger.message(
408+
"The '--repl-init-script option' is only supported starting with Scala 3.6.4 and onwards."
409+
)
410+
case _ => ()
411+
if dryRun then logger.message("Dry run, not running REPL.")
396412
else {
397-
val isAmmonite = replArtifacts.replMainClass.startsWith("ammonite")
398413
val depClassPathArgs: Seq[String] =
399414
if replArtifacts.depsClassPath.nonEmpty && !isAmmonite then
400415
Seq(
@@ -422,6 +437,7 @@ object Repl extends ScalaCommand[ReplOptions] with BuildCommandHelpers {
422437
if (retCode != 0)
423438
value(Left(new ReplError(retCode)))
424439
}
440+
}
425441

426442
def defaultArtifacts(): Either[BuildException, ReplArtifacts] = either {
427443
value {

modules/cli/src/main/scala/scala/cli/commands/shared/ScalacOptions.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ object ScalacOptions {
4848
val YScriptRunnerOption = "Yscriptrunner"
4949
private val scalacOptionsPurePrefixes = Set("V", "W", "X", "Y")
5050
private val scalacOptionsPrefixes = Set("P") ++ scalacOptionsPurePrefixes
51+
val replInitScript = "repl-init-script"
52+
private val replAliasedOptions = Set(replInitScript)
5153
private val scalacAliasedOptions = // these options don't require being passed after -O and accept an arg
5254
Set(
5355
"coverage-exclude-classlikes",
@@ -61,7 +63,7 @@ object ScalacOptions {
6163
"target",
6264
"source",
6365
YScriptRunnerOption
64-
)
66+
) ++ replAliasedOptions
6567
private val scalacNoArgAliasedOptions = // these options don't require being passed after -O and don't accept an arg
6668
Set(
6769
"experimental",
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package scala
2+
3+
import coursier.core.Version
4+
5+
package object cli {
6+
extension (s: String) def coursierVersion: Version = Version(s)
7+
}

modules/integration/src/test/scala/scala/cli/integration/ReplTestDefinitions.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,26 @@ abstract class ReplTestDefinitions extends ScalaCliSuite with TestScalaVersionAr
8686
expect(res.exitCode == 0)
8787
}
8888
}
89+
90+
test("--repl-init-script dry run") {
91+
TestInputs.empty.fromRoot { root =>
92+
val r = os.proc(
93+
TestUtil.cli,
94+
"repl",
95+
extraOptions,
96+
"--repl-init-script",
97+
"println(\"Hello\")",
98+
"--repl-dry-run"
99+
)
100+
.call(cwd = root, stderr = os.Pipe, check = false)
101+
val warningText =
102+
"The '--repl-init-script option' is only supported starting with Scala 3.6.4 and onwards."
103+
val coursierScalaVersion = actualScalaVersion.coursierVersion
104+
val shouldPrintWarning = coursierScalaVersion < "3.6.4".coursierVersion &&
105+
coursierScalaVersion < "3.6.4-RC1".coursierVersion &&
106+
coursierScalaVersion < "3.6.4-RC1-bin-20250109-a50a1e4-NIGHTLY".coursierVersion
107+
if (shouldPrintWarning) expect(r.err.trim().contains(warningText))
108+
else expect(!r.err.trim().contains(warningText))
109+
}
110+
}
89111
}

website/docs/commands/repl.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,63 @@ scala> :quit
8080

8181
</ChainedSnippets>
8282

83+
## Passing REPL options
84+
It is also possible to manually pass REPL-specific options.
85+
It can be done in a couple ways:
86+
- after the `--` separator, as the REPL itself is the launched app, so its options are app arguments
87+
88+
<ChainedSnippets>
89+
90+
```bash ignore
91+
scala repl -S 3.6.4-RC1 -- --repl-init-script 'println("Hello")'
92+
```
93+
94+
```
95+
Hello
96+
Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).
97+
Type in expressions for evaluation. Or try :help.
98+
99+
scala>
100+
```
101+
</ChainedSnippets>
102+
103+
104+
- with the `-O`, effectively passing them as compiler options:
105+
106+
<ChainedSnippets>
107+
108+
```bash ignore
109+
scala repl -S 3.6.4-RC1 -O --repl-init-script -O 'println("Hello")'
110+
```
111+
112+
```
113+
Hello
114+
Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).
115+
Type in expressions for evaluation. Or try :help.
116+
117+
scala>
118+
```
119+
120+
</ChainedSnippets>
121+
122+
- 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):
123+
124+
<ChainedSnippets>
125+
126+
```bash ignore
127+
scala repl -S 3.6.4-RC1 --repl-init-script 'println("Hello")'
128+
```
129+
130+
```
131+
Hello
132+
Welcome to Scala 3.6.4-RC1 (23.0.1, Java OpenJDK 64-Bit Server VM).
133+
Type in expressions for evaluation. Or try :help.
134+
135+
scala>
136+
```
137+
138+
</ChainedSnippets>
139+
83140
## Using Toolkit in REPL
84141
It is also possible to start the scala-cli REPL with [toolkit](https://scala-cli.virtuslab.org/docs/guides/introduction/toolkit/) enabled
85142

0 commit comments

Comments
 (0)