Skip to content

Commit 664b2e6

Browse files
authored
fixup line number reporting and simplify implementation: part 1 (mpollmeier#157)
Part 1 (this PR): predef files. Prior to this we concatenated all predef files and handed that over to the REPL/script/embedded driver for interpretation. That worked, but the line numbers were wrong, and the implementation more complicated than necessary. A followup PR will address the issue for scripts as well. That's somewhat different because we need to embed the script content in some wrapper code..
1 parent b3f3c1f commit 664b2e6

21 files changed

+284
-302
lines changed

README.md

Lines changed: 31 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Prerequisite: jdk11+
2929
* [Operators: Redirect to file, pipe to external command](#operators-redirect-to-file-pipe-to-external-command)
3030
* [Add dependencies with maven coordinates](#add-dependencies-with-maven-coordinates)
3131
* [Importing additional script files interactively](#importing-additional-script-files-interactively)
32+
* [Adding classpath entries](#adding-classpath-entries)
3233
* [Rendering of output](#rendering-of-output)
3334
* [Exiting the REPL](#exiting-the-repl)
3435
* [Looking up the current terminal width](#looking-up-the-current-terminal-width)
@@ -38,7 +39,7 @@ Prerequisite: jdk11+
3839
* [Importing files / scripts](#importing-files--scripts)
3940
* [Dependencies](#dependencies)
4041
* [@main entrypoints](#main-entrypoints)
41-
* [multiple @main entrypoints: test-main-multiple.sc](#multiple-main-entrypoints-test-main-multiplesc)
42+
* [multiple @main entrypoints](#multiple-main-entrypoints)
4243
* [named parameters](#named-parameters)
4344
- [Additional dependency resolvers and credentials](#additional-dependency-resolvers-and-credentials)
4445
* [Attach a debugger (remote jvm debug)](#attach-a-debugger-remote-jvm-debug)
@@ -60,7 +61,6 @@ Prerequisite: jdk11+
6061
* [Updating the shaded libraries](#updating-the-shaded-libraries)
6162
- [Fineprint](#fineprint)
6263

63-
6464
## Benefits over / comparison with
6565

6666
### Regular Scala REPL
@@ -136,7 +136,7 @@ All operators use the same pretty-printing that's used within the REPL, i.e. you
136136
```scala
137137
scala> case class PrettyPrintable(s: String, i: Int)
138138
scala> PrettyPrintable("two", 2) #> "out.txt"
139-
// out.txt now contains `PrettyPrintable(s = "two", i = 2)`
139+
// out.txt now contains `PrettyPrintable(s = "two", i = 2)` - in pretty colors
140140
```
141141

142142
The operators have a special handling for two common use cases that are applied at the root level of the object you hand them: list- or iterator-type objects are unwrapped and their elements are rendered in separate lines, and Strings are rendered without the surrounding `""`. Examples:
@@ -182,6 +182,7 @@ val res0: Int = 1
182182
For Scala dependencies use `::`:
183183
```
184184
srp --dep com.michaelpollmeier::colordiff:0.36
185+
185186
colordiff.ColorDiff(List("a", "b"), List("a", "bb"))
186187
// color coded diff
187188
```
@@ -196,9 +197,8 @@ echo 'val bar = "foo"' > myScript.sc
196197
197198
srp
198199
199-
val foo = 1
200200
//> using file myScript.sc
201-
println(bar) //1
201+
println(bar) //foo
202202
```
203203

204204
You can specify the filename with relative or absolute paths:
@@ -280,26 +280,17 @@ val res0: util.Try[Int] = Success(value = 202)
280280
See [ScriptRunnerTest](core/src/test/scala/replpp/scripting/ScriptRunnerTest.scala) for a more complete and in-depth overview.
281281

282282
### Simple "Hello world" script
283-
test-simple.sc
284-
```scala
285-
println("Hello!")
286-
"i was here" #> "out.txt"
287-
```
288-
289283
```bash
284+
echo 'println("Hello!")' > test-simple.sc
285+
290286
srp --script test-simple.sc
291287
cat out.txt # prints 'i was here'
292288
```
293289

294290
### Predef file(s) used in script
295-
test-predef.sc
296-
```scala
297-
println(foo)
298-
```
299-
300-
test-predef-file.sc
301-
```scala
302-
val foo = "Hello, predef file"
291+
```bash
292+
echo 'println(foo)' > test-predef.sc
293+
echo 'val foo = "Hello, predef file"' > test-predef-file.sc
303294
```
304295

305296
```bash
@@ -308,76 +299,62 @@ srp --script test-predef.sc --predef test-predef-file.sc
308299
To import multiple scripts, you can specify this parameter multiple times.
309300

310301
### Importing files / scripts
311-
foo.sc:
312-
```scala
313-
val foo = 42
314-
```
302+
```bash
303+
echo 'val foo = 42' > foo.sc
315304

316-
test.sc:
317-
```scala
318-
//> using file foo.sc
319-
println(foo)
320-
```
305+
echo '//> using file foo.sc
306+
println(foo)' > test.sc
321307

322-
```bash
323308
srp --script test.sc
324309
```
325310

326311
### Dependencies
327312
Dependencies can be added via `//> using dep` syntax (like in scala-cli).
328313

329-
test-dependencies.sc:
330-
```scala
331-
//> using dep com.michaelpollmeier:versionsort:1.0.7
314+
```bash
315+
echo '//> using dep com.michaelpollmeier:versionsort:1.0.7
332316
333317
val compareResult = versionsort.VersionHelper.compare("1.0", "0.9")
334318
assert(compareResult == 1,
335319
s"result of comparison should be `1`, but was `$compareResult`")
336-
```
320+
' > test-dependencies.sc
337321

338-
```bash
339322
srp --script test-dependencies.sc
340323
```
341324

342325
Note: this also works with `using` directives in your predef code - for script and REPL mode.
343326

344327
### @main entrypoints
345-
test-main.sc
346-
```scala
347-
@main def main() = println("Hello, world!")
348-
```
349-
350328
```bash
329+
echo '@main def main() = println("Hello, world!")' > test-main.sc
330+
351331
srp --script test-main.sc
352332
```
353333

354-
### multiple @main entrypoints: test-main-multiple.sc
355-
```scala
334+
### multiple @main entrypoints
335+
```bash
336+
echo '
356337
@main def foo() = println("foo!")
357338
@main def bar() = println("bar!")
358-
```
339+
' > test-main-multiple.sc
359340

360-
```bash
361341
srp --script test-main-multiple.sc --command foo
362342
```
363343

364344
### named parameters
365-
test-main-withargs.sc
366-
```scala
345+
```bash
346+
echo '
367347
@main def main(first: String, last: String) = {
368348
println(s"Hello, $first $last!")
369-
}
370-
```
349+
} ' > test-main-withargs.sc
371350

372-
```bash
373351
srp --script test-main-withargs.sc --param first=Michael --param last=Pollmeier
374352
```
375353
If your parameter value contains whitespace, just wrap it quotes so that your shell doesn't split it up, e.g. `--param "text=value with whitespace"`
376354

377-
Note that on windows the parameters need to be triple-quoted in any case:
355+
On windows the parameters need to be triple-quoted in any case:
378356
`srp.bat --script test-main-withargs.sc --param """first=Michael""" --param """last=Pollmeier"""`
379357

380-
381358
## Additional dependency resolvers and credentials
382359
Via `--repo` parameter on startup:
383360
```bash
@@ -388,13 +365,13 @@ To add multiple dependency resolvers, you can specify this parameter multiple ti
388365

389366
Or via `//> using resolver` directive as part of your script or predef code:
390367

391-
script-with-resolver.sc
392-
```scala
368+
```bash
369+
echo '
393370
//> using resolver https://repo.gradle.org/gradle/libs-releases
394371
//> using dep org.gradle:gradle-tooling-api:7.6.1
395372
println(org.gradle.tooling.GradleConnector.newConnector())
396-
```
397-
```scala
373+
' > script-with-resolver.sc
374+
398375
srp --script script-with-resolver.sc
399376
```
400377

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name := "root"
1+
name := "scala-repl-pp-root"
22
publish/skip := true
33

44
ThisBuild / organization := "com.michaelpollmeier"

core/src/main/scala/replpp/Config.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ case class Config(
2929
if (nocolors) Colors.BlackWhite
3030
else Colors.Default
3131

32+
def withAdditionalClasspathEntry(entry: Path): Config =
33+
copy(classpathConfig = classpathConfig.withAdditionalClasspathEntry(entry))
34+
3235
/** inverse of `Config.parse` */
3336
lazy val asJavaArgs: Seq[String] = {
3437
val args = Seq.newBuilder[String]
@@ -272,7 +275,10 @@ object Config {
272275
inheritClasspathIncludes: Seq[String] = ForClasspath.DefaultInheritClasspathIncludes,
273276
inheritClasspathExcludes: Seq[String] = Seq.empty,
274277
dependencies: Seq[String] = Seq.empty,
275-
resolvers: Seq[String] = Seq.empty)
278+
resolvers: Seq[String] = Seq.empty) {
279+
def withAdditionalClasspathEntry(entry: Path): ForClasspath =
280+
copy(additionalClasspathEntries = additionalClasspathEntries :+ util.pathAsString(entry))
281+
}
276282

277283

278284
object ForClasspath {

core/src/main/scala/replpp/Dependencies.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ object Dependencies {
2424
val repositoryArgs = additionalRepositories.flatMap { repo =>
2525
Seq("--repository", repo)
2626
}
27-
val command = Seq("java", "-jar", coursierJarPath.toAbsolutePath.toString, "fetch") ++ repositoryArgs ++ coordinates
27+
val command = Seq("java", "-jar", util.pathAsString(coursierJarPath), "fetch") ++ repositoryArgs ++ coordinates
2828
if (verbose) println(s"executing `${command.mkString(" ")}`")
2929

3030
Try(os.proc(command).call()) match {

core/src/main/scala/replpp/DottyReplDriver.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ import scala.language.implicitConversions
4444
import scala.util.control.NonFatal
4545
import scala.util.Using
4646

47-
import DottyRandomStuff.newStoreReporter
48-
4947
/** Based on https://github.com/lampepfl/dotty/blob/3.4.2/compiler/src/dotty/tools/repl/ReplDriver.scala
5048
* Main REPL instance, orchestrating input, compilation and presentation
5149
* */
@@ -60,14 +58,14 @@ class DottyReplDriver(settings: Array[String],
6058
override def sourcesRequired: Boolean = false
6159

6260
/** Create a fresh and initialized context with IDE mode enabled */
63-
private def initialCtx(settings: List[String]) = {
61+
private def initialCtx(settings: List[String]): Context = {
6462
val rootCtx = initCtx.fresh.addMode(Mode.ReadPositions | Mode.Interactive)
6563
rootCtx.setSetting(rootCtx.settings.YcookComments, true)
6664
rootCtx.setSetting(rootCtx.settings.YreadComments, true)
6765
setupRootCtx(this.settings ++ settings, rootCtx)
6866
}
6967

70-
private def setupRootCtx(settings: Array[String], rootCtx: Context) = {
68+
private def setupRootCtx(settings: Array[String], rootCtx: Context): Context = {
7169
setup(settings, rootCtx) match
7270
case Some((files, ictx)) => inContext(ictx) {
7371
shouldStart = true
@@ -224,7 +222,7 @@ class DottyReplDriver(settings: Array[String],
224222
else op
225223
}
226224

227-
private def newRun(state: State, reporter: StoreReporter = newStoreReporter) = {
225+
private def newRun(state: State, reporter: StoreReporter = DottyRandomStuff.newStoreReporter) = {
228226
val run = compiler.newRun(rootCtx.fresh.setReporter(reporter), state)
229227
state.copy(context = run.runContext)
230228
}

core/src/main/scala/replpp/InteractiveShell.scala

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@ import dotty.tools.repl.State
55
import scala.util.control.NoStackTrace
66

77
object InteractiveShell {
8-
def run(config: Config): Unit = {
98

10-
val predefCode = allPredefCode(config)
11-
val compilerArgs = replpp.compilerArgs(config)
9+
def run(config: Config): Unit = {
1210
import config.colors
11+
val config0 = precompilePredefFiles(config)
12+
13+
val compilerArgs = replpp.compilerArgs(config0)
14+
1315
val replDriver = new ReplDriver(
1416
compilerArgs,
15-
onExitCode = config.onExitCode,
16-
greeting = config.greeting,
17-
prompt = config.prompt.getOrElse("scala"),
18-
maxHeight = config.maxHeight
17+
onExitCode = config0.onExitCode,
18+
greeting = config0.greeting,
19+
prompt = config0.prompt.getOrElse("scala"),
20+
maxHeight = config0.maxHeight
1921
)
2022

2123
val initialState: State = replDriver.initialState
24+
val predefCode = DefaultPredef
2225
val state: State = {
2326
if (verboseEnabled(config)) {
2427
println(s"compiler arguments: ${compilerArgs.mkString(",")}")
@@ -35,4 +38,5 @@ object InteractiveShell {
3538

3639
replDriver.runUntilQuit(using state)()
3740
}
38-
}
41+
42+
}

core/src/main/scala/replpp/ReplDriver.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import scala.annotation.tailrec
1111
import scala.jdk.CollectionConverters.*
1212
import scala.util.{Failure, Success, Try}
1313

14-
class ReplDriver(args: Array[String],
14+
class ReplDriver(compilerArgs: Array[String],
1515
out: PrintStream = scala.Console.out,
1616
onExitCode: Option[String] = None,
1717
greeting: Option[String],
1818
prompt: String,
1919
maxHeight: Option[Int] = None,
2020
classLoader: Option[ClassLoader] = None)(using Colors)
21-
extends ReplDriverBase(args, out, maxHeight, classLoader) {
21+
extends ReplDriverBase(compilerArgs, out, maxHeight, classLoader) {
2222

2323
/** Run REPL with `state` until `:quit` command found
2424
* Main difference to the 'original': different greeting, trap Ctrl-c

core/src/main/scala/replpp/ReplDriverBase.scala

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,17 @@
11
package replpp
22

3-
import dotty.tools.MainGenericCompiler.classpathSeparator
4-
import dotty.tools.dotc.Run
5-
import dotty.tools.dotc.ast.{Positioned, tpd, untpd}
6-
import dotty.tools.dotc.classpath.{AggregateClassPath, ClassPathFactory}
7-
import dotty.tools.dotc.config.{Feature, JavaPlatform, Platform}
8-
import dotty.tools.dotc.core.Comments.{ContextDoc, ContextDocstrings}
9-
import dotty.tools.dotc.core.Contexts.{Context, ContextBase, ContextState, FreshContext, ctx, explore}
10-
import dotty.tools.dotc.core.{Contexts, MacroClassLoader, Mode, TyperState}
11-
import dotty.tools.io.{AbstractFile, ClassPath, ClassRepresentation}
123
import dotty.tools.repl.*
13-
import org.jline.reader.*
144
import replpp.shaded.fansi
155

166
import java.io.PrintStream
177
import java.lang.System.lineSeparator
18-
import java.net.URL
198
import java.nio.file.{Files, Path}
20-
import javax.naming.InitialContext
21-
import scala.annotation.tailrec
22-
import scala.collection.mutable
23-
import scala.jdk.CollectionConverters.*
24-
import scala.util.{Failure, Success, Try}
259

26-
abstract class ReplDriverBase(args: Array[String],
10+
abstract class ReplDriverBase(compilerArgs: Array[String],
2711
out: PrintStream,
2812
maxHeight: Option[Int],
2913
classLoader: Option[ClassLoader])(using Colors)
30-
extends DottyReplDriver(args, out, maxHeight, classLoader) {
14+
extends DottyReplDriver(compilerArgs, out, maxHeight, classLoader) {
3115

3216
protected def interpretInput(lines: IterableOnce[String], state: State, currentFile: Path): State = {
3317
val parsedLines = Seq.newBuilder[String]

core/src/main/scala/replpp/UsingDirectives.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ object UsingDirectives {
1313
def findImportedFilesRecursively(path: Path, visited: Set[Path] = Set.empty): Seq[Path] = {
1414
val rootDir: Path =
1515
if (Files.isDirectory(path)) path
16-
else path.getParent
16+
else path.toAbsolutePath.getParent
1717

1818
val importedFiles = findImportedFiles(util.linesFromFile(path), rootDir)
1919
val recursivelyImportedFiles = importedFiles.filterNot(visited.contains).flatMap { file =>
@@ -24,7 +24,7 @@ object UsingDirectives {
2424

2525
def findImportedFiles(lines: IterableOnce[String], rootPath: Path): Seq[Path] =
2626
scanFor(FileDirective, lines).iterator.map(resolveFile(rootPath, _)).toSeq
27-
27+
2828
def findImportedFilesRecursively(lines: IterableOnce[String], rootPath: Path): Seq[Path] = {
2929
val results = Seq.newBuilder[Path]
3030
val visited = mutable.Set.empty[Path]

0 commit comments

Comments
 (0)