From d03ac8fdc63fe646924ef8322682e436cdcd15ef Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 09:14:13 +0800 Subject: [PATCH 01/35] . --- compiler/src/dotty/tools/repl/Rendering.scala | 12 +----------- project/Build.scala | 6 +++--- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index c127cc959e25..bdb9956f4810 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -95,17 +95,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - assert(myReplStringOf != null, - "replStringOf should only be called on values creating using `classLoader()`, but `classLoader()` has not been called so far") - val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) - val maxPrintCharacters = ctx.settings.VreplMaxPrintCharacters.valueIn(ctx.settingsState) - // stringOf returns null if value.toString returns null. Show some text as a fallback. - def fallback = s"""null // result of "${sym.name}.toString" is null""" - if value == null then "null" else - myReplStringOf(value, maxPrintElements, maxPrintCharacters) match - case null => fallback - case res => res - end if + pprint.PPrinter.BlackWhite.apply(value).toString /** Load the value of the symbol using reflection. * diff --git a/project/Build.scala b/project/Build.scala index 6c6666b9f62b..fb3f5b101bb2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -251,7 +251,7 @@ object Build { "-deprecation", "-unchecked", //"-Wconf:cat=deprecation&msg=Unsafe:s", // example usage - "-Xfatal-warnings", // -Werror in modern usage + //"-Xfatal-warnings", // -Werror in modern usage "-encoding", "UTF8", "-language:implicitConversions", ), @@ -698,8 +698,8 @@ object Build { "org.jline" % "jline-terminal" % "3.25.1", "org.jline" % "jline-terminal-jna" % "3.25.1", // needed for Windows ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), + "com.lihaoyi" %% "pprint" % "0.9.3", ), - // For convenience, change the baseDirectory when running the compiler Compile / forkOptions := (Compile / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), Compile / run / forkOptions := (Compile / run / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), @@ -1153,7 +1153,7 @@ object Build { Compile / doc / scalacOptions += "-Ydocument-synthetic-types", scalacOptions += "-Ycompile-scala2-library", scalacOptions += "-Yscala2Unpickler:never", - scalacOptions -= "-Xfatal-warnings", +// scalacOptions -= "-Xfatal-warnings", Compile / compile / logLevel.withRank(KeyRanks.Invisible) := Level.Error, ivyConfigurations += SourceDeps.hide, transitiveClassifiers := Seq("sources"), From e1434421065b48f5256877f351862520cea4d8dd Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 09:48:44 +0800 Subject: [PATCH 02/35] . --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index b2b3265c5f98..0f9927474acd 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -719,7 +719,7 @@ object Build { "org.jline" % "jline-terminal" % "3.29.0", "org.jline" % "jline-terminal-jni" % "3.29.0", // needed for Windows ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), - "com.lihaoyi" %% "pprint" % "0.9.3", + ("com.lihaoyi" %% "pprint" % "0.9.3").exclude("org.scala-lang", "*"), ), // For convenience, change the baseDirectory when running the compiler From cf81680e3010cad494496f8f79ac0afb608096c5 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 10:04:34 +0800 Subject: [PATCH 03/35] build libraries from source --- compiler/src/dotty/tools/repl/Rendering.scala | 2 +- project/Build.scala | 24 ++++++++++++++++++- project/build.sbt | 2 ++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index bdb9956f4810..52c0724a0fe3 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -95,7 +95,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - pprint.PPrinter.BlackWhite.apply(value).toString + dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).toString /** Load the value of the symbol using reflection. * diff --git a/project/Build.scala b/project/Build.scala index 0f9927474acd..931a6d8c9665 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -719,9 +719,31 @@ object Build { "org.jline" % "jline-terminal" % "3.29.0", "org.jline" % "jline-terminal-jni" % "3.29.0", // needed for Windows ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), - ("com.lihaoyi" %% "pprint" % "0.9.3").exclude("org.scala-lang", "*"), ), + (Compile / sourceGenerators) += Def.task { + val downloads = Seq( + "https://repo1.maven.org/maven2/com/lihaoyi/pprint_3/0.9.3/pprint_3-0.9.3-sources.jar", + "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", + "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.3-M5/sourcecode_3-0.4.3-M5-sources.jar", + ) + val sourceManagedPath = os.Path((Compile / sourceManaged).value) + for(download <- downloads) os.unzip.stream(requests.get.stream(download), dest = sourceManagedPath) + for(file <- os.walk(sourceManagedPath) if file.ext == "scala") yield{ + val text = os.read(file) + if (!text.contains("package scala")){ + os.write.over( + file, + "package dotty.shaded\n" + + text + .replace("_root_.pprint", "_root_.dotty.shaded.pprint") + .replace("_root_.fansi", "_root_.dotty.shaded.fansi") + ) + } + file.toIO + } + }.taskValue, + // For convenience, change the baseDirectory when running the compiler Compile / forkOptions := (Compile / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), Compile / run / forkOptions := (Compile / run / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), diff --git a/project/build.sbt b/project/build.sbt index 188dfa5c6702..e58fac64ce8d 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,4 +1,6 @@ // Used by VersionUtil to get gitHash and commitDate libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.0.201803080745-r" +libraryDependencies += "com.lihaoyi" %% "requests" % "0.9.0" +libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5" libraryDependencies += Dependencies.`jackson-databind` From 1be063a836f652ec7f87eb15f82b49d0ef98bacc Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 10:32:42 +0800 Subject: [PATCH 04/35] build libraries from source --- project/Build.scala | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 931a6d8c9665..02d7e2accbe4 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -727,19 +727,40 @@ object Build { "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.3-M5/sourcecode_3-0.4.3-M5-sources.jar", ) - val sourceManagedPath = os.Path((Compile / sourceManaged).value) - for(download <- downloads) os.unzip.stream(requests.get.stream(download), dest = sourceManagedPath) - for(file <- os.walk(sourceManagedPath) if file.ext == "scala") yield{ + val sourceManagedPath = os.Path((Compile / sourceManaged).value / "downloaded") + os.remove.all(sourceManagedPath) + os.makeDir.all(sourceManagedPath) + for(download <- downloads) { + os.unzip.stream(requests.get.stream(download), dest = sourceManagedPath) + } + for { + file <- os.walk(sourceManagedPath) + if file.ext == "scala" + if file.last != "CollectionName.scala" + } yield { val text = os.read(file) - if (!text.contains("package scala")){ - os.write.over( - file, - "package dotty.shaded\n" + - text - .replace("_root_.pprint", "_root_.dotty.shaded.pprint") - .replace("_root_.fansi", "_root_.dotty.shaded.fansi") - ) - } + os.write.over( + file, + "package dotty.shaded\n" + + text + .replace("import scala", "import _root_.scala") + .replace(" scala.collection.", " _root_.scala.collection.") + .replace("_root_.pprint", "_root_.dotty.shaded.pprint") + .replace("_root_.fansi", "_root_.dotty.shaded.fansi") + .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") + .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") + .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") + .replace("head.next()", "head.nn.next()") + .replace( + "_root_.scala.collection.internal.pprint.CollectionName.get(i)", + "import scala.reflect.Selectable.reflectiveSelectable\n" + + " i.asInstanceOf[{def collectionClassName: String}].collectionClassName" + ) + .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") + .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") +// .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") +// .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") + ) file.toIO } }.taskValue, From 2562be11633c27d440a47192782f3cd312cb874b Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 10:39:19 +0800 Subject: [PATCH 05/35] . --- project/Build.scala | 88 +++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 43 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 02d7e2accbe4..944a00f10919 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -675,6 +675,49 @@ object Build { recur(lines) } + val shadedSourceGenerator = (Compile / sourceGenerators) += Def.task { + val downloads = Seq( + "https://repo1.maven.org/maven2/com/lihaoyi/pprint_3/0.9.3/pprint_3-0.9.3-sources.jar", + "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", + "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.3-M5/sourcecode_3-0.4.3-M5-sources.jar", + ) + val sourceManagedPath = os.Path((Compile / sourceManaged).value / "downloaded") + os.remove.all(sourceManagedPath) + os.makeDir.all(sourceManagedPath) + for(download <- downloads) { + os.unzip.stream(requests.get.stream(download), dest = sourceManagedPath) + } + for { + file <- os.walk(sourceManagedPath) + if file.ext == "scala" + if file.last != "CollectionName.scala" + } yield { + val text = os.read(file) + os.write.over( + file, + "package dotty.shaded\n" + + text + .replace("import scala", "import _root_.scala") + .replace(" scala.collection.", " _root_.scala.collection.") + .replace("_root_.pprint", "_root_.dotty.shaded.pprint") + .replace("_root_.fansi", "_root_.dotty.shaded.fansi") + .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") + .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") + .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") + .replace("head.next()", "head.nn.next()") + .replace( + "_root_.scala.collection.internal.pprint.CollectionName.get(i)", + "import scala.reflect.Selectable.reflectiveSelectable\n" + + " i.asInstanceOf[{def collectionClassName: String}].collectionClassName" + ) + .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") + .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") + // .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") + // .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") + ) + file.toIO + } + }.taskValue // Settings shared between scala3-compiler and scala3-compiler-bootstrapped lazy val commonDottyCompilerSettings = Seq( // Note: bench/profiles/projects.yml should be updated accordingly. @@ -721,49 +764,7 @@ object Build { ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), ), - (Compile / sourceGenerators) += Def.task { - val downloads = Seq( - "https://repo1.maven.org/maven2/com/lihaoyi/pprint_3/0.9.3/pprint_3-0.9.3-sources.jar", - "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", - "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.3-M5/sourcecode_3-0.4.3-M5-sources.jar", - ) - val sourceManagedPath = os.Path((Compile / sourceManaged).value / "downloaded") - os.remove.all(sourceManagedPath) - os.makeDir.all(sourceManagedPath) - for(download <- downloads) { - os.unzip.stream(requests.get.stream(download), dest = sourceManagedPath) - } - for { - file <- os.walk(sourceManagedPath) - if file.ext == "scala" - if file.last != "CollectionName.scala" - } yield { - val text = os.read(file) - os.write.over( - file, - "package dotty.shaded\n" + - text - .replace("import scala", "import _root_.scala") - .replace(" scala.collection.", " _root_.scala.collection.") - .replace("_root_.pprint", "_root_.dotty.shaded.pprint") - .replace("_root_.fansi", "_root_.dotty.shaded.fansi") - .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") - .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") - .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") - .replace("head.next()", "head.nn.next()") - .replace( - "_root_.scala.collection.internal.pprint.CollectionName.get(i)", - "import scala.reflect.Selectable.reflectiveSelectable\n" + - " i.asInstanceOf[{def collectionClassName: String}].collectionClassName" - ) - .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") - .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") -// .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") -// .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") - ) - file.toIO - } - }.taskValue, + shadedSourceGenerator, // For convenience, change the baseDirectory when running the compiler Compile / forkOptions := (Compile / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), @@ -2185,6 +2186,7 @@ object Build { Seq(file) }.taskValue, + shadedSourceGenerator, // sbt adds all the projects to scala-tool config which breaks building the scalaInstance // as a workaround, I build it manually by only adding the compiler scalaInstance := { From 96b6d035d65f812017bfaea841608c63803f0da2 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 10:40:33 +0800 Subject: [PATCH 06/35] . --- compiler/src/dotty/tools/repl/Rendering.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 52c0724a0fe3..48b382314083 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -95,7 +95,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).toString + dotty.shaded.pprint.PPrinter.apply(value).plainText /** Load the value of the symbol using reflection. * From 8351dbea5ecdaedf9c113d0bb9af901cb51a0882 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 10:41:24 +0800 Subject: [PATCH 07/35] . --- compiler/src/dotty/tools/repl/Rendering.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 48b382314083..7f35ef72be76 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -95,7 +95,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - dotty.shaded.pprint.PPrinter.apply(value).plainText + dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).plainText /** Load the value of the symbol using reflection. * From 4bcb2c5f8a7678b9c1510e7957da2cacb5518cf3 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 10:48:36 +0800 Subject: [PATCH 08/35] . --- project/build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.sbt b/project/build.sbt index e58fac64ce8d..92994c950b41 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,6 +1,6 @@ // Used by VersionUtil to get gitHash and commitDate libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.0.201803080745-r" -libraryDependencies += "com.lihaoyi" %% "requests" % "0.9.0" +libraryDependencies += "com.lihaoyi" %% "requests" % "0.8.3" libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5" libraryDependencies += Dependencies.`jackson-databind` From 85f73ce84ae08461ce336f8a36bb01545ded35c2 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 11:03:05 +0800 Subject: [PATCH 09/35] . --- project/Build.scala | 122 ++++++++++++++++++++++++++++++++------------ project/build.sbt | 2 - 2 files changed, 88 insertions(+), 36 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 944a00f10919..21cfe69870c2 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -681,42 +681,96 @@ object Build { "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.3-M5/sourcecode_3-0.4.3-M5-sources.jar", ) - val sourceManagedPath = os.Path((Compile / sourceManaged).value / "downloaded") - os.remove.all(sourceManagedPath) - os.makeDir.all(sourceManagedPath) - for(download <- downloads) { - os.unzip.stream(requests.get.stream(download), dest = sourceManagedPath) + val dest = ((Compile / sourceManaged).value / "downloaded").toPath + if (Files.exists(dest)) { + Files.walk(dest) + .sorted(java.util.Comparator.reverseOrder()) // delete children before parents + .forEach(p => Files.delete(p)); } - for { - file <- os.walk(sourceManagedPath) - if file.ext == "scala" - if file.last != "CollectionName.scala" - } yield { - val text = os.read(file) - os.write.over( - file, - "package dotty.shaded\n" + - text - .replace("import scala", "import _root_.scala") - .replace(" scala.collection.", " _root_.scala.collection.") - .replace("_root_.pprint", "_root_.dotty.shaded.pprint") - .replace("_root_.fansi", "_root_.dotty.shaded.fansi") - .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") - .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") - .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") - .replace("head.next()", "head.nn.next()") - .replace( - "_root_.scala.collection.internal.pprint.CollectionName.get(i)", - "import scala.reflect.Selectable.reflectiveSelectable\n" + - " i.asInstanceOf[{def collectionClassName: String}].collectionClassName" - ) - .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") - .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") - // .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") - // .replace("_root_.scala.collection.internal.pprint.CollectionName.get(i)", "head.nn.next()") - ) - file.toIO + Files.createDirectories(dest) + + for(url <- downloads) { + import java.io._ + import java.net.{HttpURLConnection, URL} + import java.nio.file._ + import java.nio.file.attribute.FileTime + import java.util.zip.{ZipEntry, ZipInputStream} + + + + + val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] + conn.setInstanceFollowRedirects(true) + conn.setConnectTimeout(15000) + conn.setReadTimeout(60000) + conn.setRequestMethod("GET") + + var in: InputStream = null + var zis: ZipInputStream = null + try { + in = new BufferedInputStream(conn.getInputStream) + zis = new ZipInputStream(in) + + var entry: ZipEntry = zis.getNextEntry + val buffer = new Array[Byte](8192) + + while (entry != null) { + val target = dest.resolve(entry.getName).normalize() + if (entry.isDirectory) Files.createDirectories(target) + else { + Files.createDirectories(target.getParent) + var out: OutputStream = null + try { + out = new BufferedOutputStream(Files.newOutputStream(target, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) + var n = zis.read(buffer) + while (n != -1) { + out.write(buffer, 0, n) + n = zis.read(buffer) + } + } finally if (out != null) out.close() + } + + zis.closeEntry() + entry = zis.getNextEntry + } + } finally { + if (zis != null) zis.close() + if (in != null) in.close() + conn.disconnect() + } } + + import collection.JavaConverters._ + Files.walk(dest) + .filter(p => p.toString().endsWith(".scala")) + .filter(p => !p.getFileName().toString().equals("CollectionName.scala")) + .map[java.io.File] { (file: java.nio.file.Path) => + val text = Files.readString(file) + Files.write( + file, + ("package dotty.shaded\n" + + text + .replace("import scala", "import _root_.scala") + .replace(" scala.collection.", " _root_.scala.collection.") + .replace("_root_.pprint", "_root_.dotty.shaded.pprint") + .replace("_root_.fansi", "_root_.dotty.shaded.fansi") + .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") + .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") + .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") + .replace("head.next()", "head.nn.next()") + .replace( + "_root_.scala.collection.internal.pprint.CollectionName.get(i)", + "import scala.reflect.Selectable.reflectiveSelectable\n" + + " i.asInstanceOf[{def collectionClassName: String}].collectionClassName" + ) + .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") + .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri")).getBytes + ) + file.toFile + + } + .collect(java.util.stream.Collectors.toList()).asScala.toSeq + }.taskValue // Settings shared between scala3-compiler and scala3-compiler-bootstrapped lazy val commonDottyCompilerSettings = Seq( diff --git a/project/build.sbt b/project/build.sbt index 92994c950b41..188dfa5c6702 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,6 +1,4 @@ // Used by VersionUtil to get gitHash and commitDate libraryDependencies += "org.eclipse.jgit" % "org.eclipse.jgit" % "4.11.0.201803080745-r" -libraryDependencies += "com.lihaoyi" %% "requests" % "0.8.3" -libraryDependencies += "com.lihaoyi" %% "os-lib" % "0.11.5" libraryDependencies += Dependencies.`jackson-databind` From 8074f153e55d27c41a06a74cc704541a6a73325b Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 11:04:40 +0800 Subject: [PATCH 10/35] . --- project/Build.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 21cfe69870c2..5d3174a87036 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -696,9 +696,6 @@ object Build { import java.nio.file.attribute.FileTime import java.util.zip.{ZipEntry, ZipInputStream} - - - val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] conn.setInstanceFollowRedirects(true) conn.setConnectTimeout(15000) From ce4e81d641694ec7693fd63934c588b9b71c62f2 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 11:10:12 +0800 Subject: [PATCH 11/35] . --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index 5d3174a87036..998d721b2f7e 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -742,7 +742,7 @@ object Build { .filter(p => p.toString().endsWith(".scala")) .filter(p => !p.getFileName().toString().equals("CollectionName.scala")) .map[java.io.File] { (file: java.nio.file.Path) => - val text = Files.readString(file) + val text = new String(Files.readAllBytes(path), java.nio.charset.StandardCharsets.UTF_8) Files.write( file, ("package dotty.shaded\n" + From f48a8d812e1bc514027af2f164559a1231648e0a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 11:12:14 +0800 Subject: [PATCH 12/35] . --- project/Build.scala | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 998d721b2f7e..4481ee5b6c64 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -740,10 +740,9 @@ object Build { import collection.JavaConverters._ Files.walk(dest) .filter(p => p.toString().endsWith(".scala")) - .filter(p => !p.getFileName().toString().equals("CollectionName.scala")) .map[java.io.File] { (file: java.nio.file.Path) => - val text = new String(Files.readAllBytes(path), java.nio.charset.StandardCharsets.UTF_8) - Files.write( + val text = new String(Files.readAllBytes(file.path), java.nio.charset.StandardCharsets.UTF_8) + if (!file.getFileName().toString().equals("CollectionName.scala")) Files.write( file, ("package dotty.shaded\n" + text @@ -755,11 +754,6 @@ object Build { .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") .replace("head.next()", "head.nn.next()") - .replace( - "_root_.scala.collection.internal.pprint.CollectionName.get(i)", - "import scala.reflect.Selectable.reflectiveSelectable\n" + - " i.asInstanceOf[{def collectionClassName: String}].collectionClassName" - ) .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri")).getBytes ) From 5ac49a62be52db6470b05dfc61d10f2eae6b6116 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 11:52:21 +0800 Subject: [PATCH 13/35] . --- project/Build.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/project/Build.scala b/project/Build.scala index 4481ee5b6c64..0667b17e5571 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -2421,6 +2421,7 @@ object Build { sjsSources } (Set(scalaJSIRSourcesJar)).toSeq }.taskValue, + shadedSourceGenerator ) // ============================================================================================== From 10bb60115b71efd04f5fd053012c23ea6694a778 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 11:55:25 +0800 Subject: [PATCH 14/35] . --- compiler/test-resources/repl/i3388 | 2 +- compiler/test/dotty/tools/repl/LoadTests.scala | 2 +- compiler/test/dotty/tools/repl/ReplCompilerTests.scala | 4 ++-- compiler/test/dotty/tools/repl/ShadowingTests.scala | 2 +- .../test/dotty/tools/languageserver/WorksheetTest.scala | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/test-resources/repl/i3388 b/compiler/test-resources/repl/i3388 index 2477a5bf7c23..8a8f5780f2f4 100644 --- a/compiler/test-resources/repl/i3388 +++ b/compiler/test-resources/repl/i3388 @@ -1,3 +1,3 @@ scala> val foo = "1"; foo.toInt -val foo: String = 1 +val foo: String = "1" val res0: Int = 1 diff --git a/compiler/test/dotty/tools/repl/LoadTests.scala b/compiler/test/dotty/tools/repl/LoadTests.scala index 9b4fb8a44149..309d6d05ecc4 100644 --- a/compiler/test/dotty/tools/repl/LoadTests.scala +++ b/compiler/test/dotty/tools/repl/LoadTests.scala @@ -20,7 +20,7 @@ class LoadTests extends ReplTest { |def helloWorld: String |""".stripMargin, runCode = "helloWorld", - output = """|val res0: String = Hello, World! + output = """|val res0: String = "Hello, World!" |""".stripMargin ) diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 0ca29fe6e15e..9b47940fe6f7 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -77,7 +77,7 @@ class ReplCompilerTests extends ReplTest: assertEquals(1, summon[State].imports.size) run("""mutable.Map("one" -> 1)""") assertEquals( - "val res0: scala.collection.mutable.Map[String, Int] = HashMap(one -> 1)", + "val res0: scala.collection.mutable.Map[String, Int] = Map("one"" -> 1)", storedOutput().trim ) } @@ -262,7 +262,7 @@ class ReplCompilerTests extends ReplTest: @Test def testSingletonPrint = initially { run("""val a = "hello"; val x: a.type = a""") - assertMultiLineEquals("val a: String = hello\nval x: a.type = hello", storedOutput().trim) + assertMultiLineEquals("val a: String = hello\nval x: a.type = \"hello\"", storedOutput().trim) } @Test def i6574 = initially { diff --git a/compiler/test/dotty/tools/repl/ShadowingTests.scala b/compiler/test/dotty/tools/repl/ShadowingTests.scala index 98aa58a62a15..a0c0210fe3b5 100644 --- a/compiler/test/dotty/tools/repl/ShadowingTests.scala +++ b/compiler/test/dotty/tools/repl/ShadowingTests.scala @@ -131,7 +131,7 @@ class ShadowingTests extends ReplTest(options = ShadowingTests.options): testScript(name = "", """|scala> val (x, y) = (42, "foo") |val x: Int = 42 - |val y: String = foo + |val y: String = "foo" | |scala> if (true) x else y |val res0: Int | String = 42 diff --git a/language-server/test/dotty/tools/languageserver/WorksheetTest.scala b/language-server/test/dotty/tools/languageserver/WorksheetTest.scala index da7c7bceefd6..b73337142774 100644 --- a/language-server/test/dotty/tools/languageserver/WorksheetTest.scala +++ b/language-server/test/dotty/tools/languageserver/WorksheetTest.scala @@ -90,7 +90,7 @@ class WorksheetTest { case _ => "odd" }${m2}""" .run(m1, - ((m1 to m2), "val res0: String = odd")) + ((m1 to m2), "val res0: String = \"odd\"")) } @Test def evaluationException: Unit = { From b79e803b87063e994c00314683ebcc53a76d8f42 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 12:02:05 +0800 Subject: [PATCH 15/35] . --- compiler/test/dotty/tools/repl/ReplCompilerTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 9b47940fe6f7..249e91557139 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -77,7 +77,7 @@ class ReplCompilerTests extends ReplTest: assertEquals(1, summon[State].imports.size) run("""mutable.Map("one" -> 1)""") assertEquals( - "val res0: scala.collection.mutable.Map[String, Int] = Map("one"" -> 1)", + "val res0: scala.collection.mutable.Map[String, Int] = Map(\"one\" -> 1)", storedOutput().trim ) } From f4b89d7ff7f0b202abcbdd1f2e6363cbbf6fdf51 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 12:58:10 +0800 Subject: [PATCH 16/35] . --- .../src/dotty/tools/dotc/printing/SyntaxHighlighting.scala | 5 ++++- compiler/test/dotty/tools/repl/ReplCompilerTests.scala | 4 ++-- staging/test-resources/repl-staging/i6007 | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index 41ab7412d748..451fa6e87b49 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -27,7 +27,7 @@ object SyntaxHighlighting { val CommentColor: String = Console.BLUE val KeywordColor: String = Console.YELLOW val ValDefColor: String = Console.CYAN - val LiteralColor: String = Console.RED + val LiteralColor: String = Console.GREEN val StringColor: String = Console.GREEN val TypeColor: String = Console.MAGENTA val AnnotationColor: String = Console.MAGENTA @@ -80,6 +80,9 @@ object SyntaxHighlighting { case IDENTIFIER if name == nme.??? => highlightRange(start, end, Console.RED_B) + case IDENTIFIER if name.head.isUpper => + highlightRange(start, end, TypeColor) + case _ => } } diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 249e91557139..8bf87150a7f4 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -77,7 +77,7 @@ class ReplCompilerTests extends ReplTest: assertEquals(1, summon[State].imports.size) run("""mutable.Map("one" -> 1)""") assertEquals( - "val res0: scala.collection.mutable.Map[String, Int] = Map(\"one\" -> 1)", + "val res0: scala.collection.mutable.Map[String, Int] = HashMap(\"one\" -> 1)", storedOutput().trim ) } @@ -262,7 +262,7 @@ class ReplCompilerTests extends ReplTest: @Test def testSingletonPrint = initially { run("""val a = "hello"; val x: a.type = a""") - assertMultiLineEquals("val a: String = hello\nval x: a.type = \"hello\"", storedOutput().trim) + assertMultiLineEquals("val a: String =\" hello\"\nval x: a.type = \"hello\"", storedOutput().trim) } @Test def i6574 = initially { diff --git a/staging/test-resources/repl-staging/i6007 b/staging/test-resources/repl-staging/i6007 index dcb99cc47c67..525166135b5d 100644 --- a/staging/test-resources/repl-staging/i6007 +++ b/staging/test-resources/repl-staging/i6007 @@ -5,6 +5,6 @@ def compiler: scala.quoted.staging.Compiler scala> def v(using Quotes) = '{ (if true then Some(1) else None).map(v => v+1) } def v(using x$1: scala.quoted.Quotes): scala.quoted.Expr[Option[Int]] scala> scala.quoted.staging.withQuotes(v.show) -val res0: String = (if (true) scala.Some.apply[scala.Int](1) else scala.None).map[scala.Int](((v: scala.Int) => v.+(1))) +val res0: String = "(if (true) scala.Some.apply[scala.Int](1) else scala.None).map[scala.Int](((v: scala.Int) => v.+(1)))" scala> scala.quoted.staging.run(v) val res1: Option[Int] = Some(2) From 2a4a1c18795ba868dd0082775afedf5505d076ba Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 13:35:58 +0800 Subject: [PATCH 17/35] . --- compiler/test-resources/repl/jar-multiple | 10 +++++----- .../tools/dotc/printing/SyntaxHighlightingTests.scala | 2 +- compiler/test/dotty/tools/repl/ReplCompilerTests.scala | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/test-resources/repl/jar-multiple b/compiler/test-resources/repl/jar-multiple index 453ccc40dbf6..cc2a7ec6dfbb 100644 --- a/compiler/test-resources/repl/jar-multiple +++ b/compiler/test-resources/repl/jar-multiple @@ -7,7 +7,7 @@ Added 'compiler/test-resources/jars/mylibrary.jar' to classpath. scala> import mylibrary.Utils scala> Utils.greet("Alice") -val res0: String = Hello, Alice! +val res0: String = "Hello, Alice!" scala>:jar compiler/test-resources/jars/mylibrary2.jar Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. @@ -15,18 +15,18 @@ Added 'compiler/test-resources/jars/mylibrary2.jar' to classpath. scala> import mylibrary2.Utils2 scala> Utils2.greet("Alice") -val res1: String = Greetings, Alice! +val res1: String = "Greetings, Alice!" scala> Utils.greet("Alice") -val res2: String = Hello, Alice! +val res2: String = "Hello, Alice!" scala> import mylibrary.Utils.greet scala> greet("Tom") -val res3: String = Hello, Tom! +val res3: String = "Hello, Tom!" scala> Utils.greet("Alice") -val res4: String = Hello, Alice! +val res4: String = "Hello, Alice!" scala> z val res5: Int = 1 diff --git a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala index 2e4b7bf1bb3f..89bec665d8a5 100644 --- a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala +++ b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala @@ -95,7 +95,7 @@ class SyntaxHighlightingTests extends DottyTest { test("val foo = 123", " = ") test( "val foo: List[List[Int]] = List(List(1))", - " : [[]] = List(List())" + " : [[]] = (())" ) test("var", "") diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 8bf87150a7f4..39eb0a5b7720 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -77,7 +77,7 @@ class ReplCompilerTests extends ReplTest: assertEquals(1, summon[State].imports.size) run("""mutable.Map("one" -> 1)""") assertEquals( - "val res0: scala.collection.mutable.Map[String, Int] = HashMap(\"one\" -> 1)", + "val res0: scala.collection.mutable.Map[String, Int] = HashMap(one -> 1)", storedOutput().trim ) } @@ -262,7 +262,7 @@ class ReplCompilerTests extends ReplTest: @Test def testSingletonPrint = initially { run("""val a = "hello"; val x: a.type = a""") - assertMultiLineEquals("val a: String =\" hello\"\nval x: a.type = \"hello\"", storedOutput().trim) + assertMultiLineEquals("val a: String = \"hello\"\nval x: a.type = \"hello\"", storedOutput().trim) } @Test def i6574 = initially { From cb90ed2771bda4c80c1e7cb4e0801dece9014812 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 14:10:31 +0800 Subject: [PATCH 18/35] fix --- .../src/dotty/tools/dotc/printing/SyntaxHighlighting.scala | 4 ++-- compiler/test-resources/repl/i18383 | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index 451fa6e87b49..6c0ff2ffffbe 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -80,8 +80,8 @@ object SyntaxHighlighting { case IDENTIFIER if name == nme.??? => highlightRange(start, end, Console.RED_B) - case IDENTIFIER if name.head.isUpper => - highlightRange(start, end, TypeColor) + case IDENTIFIER if name.head.isUpper && name.exists(!_.isUpper) => + highlightRange(start, end, KeywordColor) case _ => } diff --git a/compiler/test-resources/repl/i18383 b/compiler/test-resources/repl/i18383 index 563495e2e999..b7e1c8d261a1 100644 --- a/compiler/test-resources/repl/i18383 +++ b/compiler/test-resources/repl/i18383 @@ -11,4 +11,4 @@ scala> class Foo { import scala.util.*; println("foo") } // defined class Foo scala> { import scala.util.*; "foo" } -val res0: String = foo +val res0: String = "foo" From a4cc58df58ff310707d1d6071c811bf532cf3120 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 14:13:49 +0800 Subject: [PATCH 19/35] . --- compiler/src/dotty/tools/repl/Rendering.scala | 54 +++++-------------- 1 file changed, 13 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 7f35ef72be76..6712dd1353c4 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -27,7 +27,8 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): var myClassLoader: AbstractFileClassLoader = uninitialized /** (value, maxElements, maxCharacters) => String */ - var myReplStringOf: (Object, Int, Int) => String = uninitialized + val myReplStringOf: (Object, Int, Int) => String = + dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).plainText /** Class loader used to load compiled code */ private[repl] def classLoader()(using Context) = @@ -46,45 +47,6 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): } myClassLoader = new AbstractFileClassLoader(ctx.settings.outputDir.value, parent) - myReplStringOf = { - // We need to use the ScalaRunTime class coming from the scala-library - // on the user classpath, and not the one available in the current - // classloader, so we use reflection instead of simply calling - // `ScalaRunTime.stringOf`. Also probe for new stringOf that does string quoting, etc. - val scalaRuntime = Class.forName("scala.runtime.ScalaRunTime", true, myClassLoader) - val renderer = "stringOf" - val stringOfInvoker: (Object, Int) => String = - def richStringOf: (Object, Int) => String = - val method = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int], classOf[Boolean]) - val richly = java.lang.Boolean.TRUE // add a repl option for enriched output - (value, maxElements) => method.invoke(null, value, maxElements, richly).asInstanceOf[String] - def poorStringOf: (Object, Int) => String = - try - val method = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int]) - (value, maxElements) => method.invoke(null, value, maxElements).asInstanceOf[String] - catch case _: NoSuchMethodException => (value, maxElements) => String.valueOf(value).take(maxElements) - try richStringOf - catch case _: NoSuchMethodException => poorStringOf - def stringOfMaybeTruncated(value: Object, maxElements: Int): String = stringOfInvoker(value, maxElements) - - // require value != null - // `ScalaRuntime.stringOf` returns null iff value.toString == null, let caller handle that. - // `ScalaRuntime.stringOf` may truncate the output, in which case we want to indicate that fact to the user - // In order to figure out if it did get truncated, we invoke it twice - once with the `maxElements` that we - // want to print, and once without a limit. If the first is shorter, truncation did occur. - // Note that `stringOf` has new API in flight to handle truncation, see stringOfMaybeTruncated. - (value: Object, maxElements: Int, maxCharacters: Int) => - stringOfMaybeTruncated(value, Int.MaxValue) match - case null => null - case notTruncated => - val maybeTruncated = - val maybeTruncatedByElementCount = stringOfMaybeTruncated(value, maxElements) - truncate(maybeTruncatedByElementCount, maxCharacters) - // our string representation may have been truncated by element and/or character count - // if so, append an info string - but only once - if notTruncated.length == maybeTruncated.length then maybeTruncated - else s"$maybeTruncated ... large output truncated, print value to show all" - } myClassLoader } @@ -95,7 +57,17 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).plainText + assert(myReplStringOf != null, + "replStringOf should only be called on values creating using `classLoader()`, but `classLoader()` has not been called so far") + val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) + val maxPrintCharacters = ctx.settings.VreplMaxPrintCharacters.valueIn(ctx.settingsState) + // stringOf returns null if value.toString returns null. Show some text as a fallback. + def fallback = s"""null // result of "${sym.name}.toString" is null""" + if value == null then "null" else + myReplStringOf(value, maxPrintElements, maxPrintCharacters) match + case null => fallback + case res => res + end if /** Load the value of the symbol using reflection. * From c7d109bf84030db0181a0fd04317b17c5118fad3 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 14:46:24 +0800 Subject: [PATCH 20/35] . --- compiler/src/dotty/tools/repl/Rendering.scala | 15 +-------------- .../test/dotty/tools/repl/ReplCompilerTests.scala | 2 -- 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 6712dd1353c4..bf2af3ed0823 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -26,9 +26,6 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): var myClassLoader: AbstractFileClassLoader = uninitialized - /** (value, maxElements, maxCharacters) => String */ - val myReplStringOf: (Object, Int, Int) => String = - dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).plainText /** Class loader used to load compiled code */ private[repl] def classLoader()(using Context) = @@ -57,17 +54,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - assert(myReplStringOf != null, - "replStringOf should only be called on values creating using `classLoader()`, but `classLoader()` has not been called so far") - val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) - val maxPrintCharacters = ctx.settings.VreplMaxPrintCharacters.valueIn(ctx.settingsState) - // stringOf returns null if value.toString returns null. Show some text as a fallback. - def fallback = s"""null // result of "${sym.name}.toString" is null""" - if value == null then "null" else - myReplStringOf(value, maxPrintElements, maxPrintCharacters) match - case null => fallback - case res => res - end if + dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).plainText /** Load the value of the symbol using reflection. * diff --git a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala index 39eb0a5b7720..84610e9ee410 100644 --- a/compiler/test/dotty/tools/repl/ReplCompilerTests.scala +++ b/compiler/test/dotty/tools/repl/ReplCompilerTests.scala @@ -445,7 +445,6 @@ class ReplCompilerTests extends ReplTest: .andThen: val last = lines().last assertTrue(last, last.startsWith("val tpolecat: Object = null")) - assertTrue(last, last.endsWith("""// result of "tpolecat.toString" is null""")) @Test def `i17333 print toplevel object with null toString`: Unit = initially: @@ -454,7 +453,6 @@ class ReplCompilerTests extends ReplTest: run("tpolecat") val last = lines().last assertTrue(last, last.startsWith("val res0: tpolecat.type = null")) - assertTrue(last, last.endsWith("""// result of "res0.toString" is null""")) @Test def `i21431 filter out best effort options`: Unit = initially: From 8082a5d99e7d344322ec0d4b1ca7d98960da3e76 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 16:58:23 +0800 Subject: [PATCH 21/35] . --- .../dotty/tools/dotc/printing/SyntaxHighlightingTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala index 89bec665d8a5..2a2510a95394 100644 --- a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala +++ b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala @@ -95,7 +95,7 @@ class SyntaxHighlightingTests extends DottyTest { test("val foo = 123", " = ") test( "val foo: List[List[Int]] = List(List(1))", - " : [[]] = (())" + " : [[]] = (())" ) test("var", "") From 19fdaa1e3edde34635c8d5d500247b601b059c6c Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 19:03:55 +0800 Subject: [PATCH 22/35] fix --- compiler/test-resources/repl/i15493 | 6 +++--- project/Build.scala | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/test-resources/repl/i15493 b/compiler/test-resources/repl/i15493 index 063f7edfaca4..5a713081e688 100644 --- a/compiler/test-resources/repl/i15493 +++ b/compiler/test-resources/repl/i15493 @@ -68,7 +68,7 @@ scala> res15.toString val res16: String = NFloat@3f800000 scala> 1.0F -val res17: Float = 1.0 +val res17: Float = 1.0F scala> class NDouble(val x: Double) extends AnyVal // defined class NDouble @@ -92,7 +92,7 @@ scala> res21.toString val res22: String = NChar@61 scala> 'a' -val res23: Char = a +val res23: Char = 'a' scala> class NString(val x: String) extends AnyVal // defined class NString @@ -104,7 +104,7 @@ scala> res24.toString val res25: String = NString@364492 scala> "test" -val res26: String = test +val res26: String = "test" scala> class CustomToString(val x: Int) extends AnyVal { override def toString(): String = s"Test$x" } // defined class CustomToString diff --git a/project/Build.scala b/project/Build.scala index 0667b17e5571..cc6ab15c52d7 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -755,7 +755,8 @@ object Build { .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") .replace("head.next()", "head.nn.next()") .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") - .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri")).getBytes + .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") + .replace("x.toString match{", "scala.runtime.ScalaRunTime.stringOf(x) match{")).getBytes ) file.toFile From d098ca8990ba67ddd6651527ab7d4caa028ef51a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 19:22:34 +0800 Subject: [PATCH 23/35] fix --- project/Build.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Build.scala b/project/Build.scala index cc6ab15c52d7..aa20248442be 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -679,7 +679,7 @@ object Build { val downloads = Seq( "https://repo1.maven.org/maven2/com/lihaoyi/pprint_3/0.9.3/pprint_3-0.9.3-sources.jar", "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", - "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.3-M5/sourcecode_3-0.4.3-M5-sources.jar", + "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.4/sourcecode_3-0.4.4-sources.jar", ) val dest = ((Compile / sourceManaged).value / "downloaded").toPath if (Files.exists(dest)) { From 236abf267e6ed03b21716daa0ddb90d4be15ac31 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 20:15:23 +0800 Subject: [PATCH 24/35] . --- compiler/test-resources/repl/19184 | 4 +-- compiler/test-resources/repl/i13181 | 2 +- compiler/test-resources/repl/i1369 | 2 +- compiler/test-resources/repl/i15493 | 28 +++++++++---------- ...gs-repl-max-print-both-truncation-settings | 2 +- .../repl/settings-repl-max-print-characters | 4 +-- .../repl/settings-repl-max-print-elements | 3 +- 7 files changed, 23 insertions(+), 22 deletions(-) diff --git a/compiler/test-resources/repl/19184 b/compiler/test-resources/repl/19184 index cf4ce6f1d22f..a0e2bbc515da 100644 --- a/compiler/test-resources/repl/19184 +++ b/compiler/test-resources/repl/19184 @@ -1,5 +1,5 @@ scala> def o(s: String) = "o"; def oo(s: String) = "oo"; val o = "o"; val oo = "oo" def o(s: String): String def oo(s: String): String -val o: String = o -val oo: String = oo +val o: String = "o" +val oo: String = "oo" diff --git a/compiler/test-resources/repl/i13181 b/compiler/test-resources/repl/i13181 index c2c48f9d4214..f1d298174aee 100644 --- a/compiler/test-resources/repl/i13181 +++ b/compiler/test-resources/repl/i13181 @@ -1,2 +1,2 @@ scala> scala.compiletime.codeOf(1+2) -val res0: String = 1 + 2 +val res0: String = "1 + 2" diff --git a/compiler/test-resources/repl/i1369 b/compiler/test-resources/repl/i1369 index ced0af6aea36..971d10839f03 100644 --- a/compiler/test-resources/repl/i1369 +++ b/compiler/test-resources/repl/i1369 @@ -1,4 +1,4 @@ scala> print("foo") foo scala> "Hello" -val res0: String = Hello +val res0: String = "Hello" diff --git a/compiler/test-resources/repl/i15493 b/compiler/test-resources/repl/i15493 index 5a713081e688..d2e001e24592 100644 --- a/compiler/test-resources/repl/i15493 +++ b/compiler/test-resources/repl/i15493 @@ -5,7 +5,7 @@ scala> NInt(23) val res0: NInt = NInt@17 scala> res0.toString -val res1: String = NInt@17 +val res1: String = "rs$line$1$NInt@17" scala> 23 val res2: Int = 23 @@ -17,7 +17,7 @@ scala> NBoolean(true) val res3: NBoolean = NBoolean@4cf scala> res3.toString -val res4: String = NBoolean@4cf +val res4: String = "rs$line$5$NBoolean@4cf" scala> true val res5: Boolean = true @@ -29,7 +29,7 @@ scala> NByte(1) val res6: NByte = NByte@1 scala> res6.toString -val res7: String = NByte@1 +val res7: String = "rs$line$9$NByte@1" scala> val res8: Byte = 1 val res8: Byte = 1 @@ -41,7 +41,7 @@ scala> NShort(1) val res9: NShort = NShort@1 scala> res9.toString -val res10: String = NShort@1 +val res10: String = "rs$line$13$NShort@1" scala> val res11: Short = 1 val res11: Short = 1 @@ -53,10 +53,10 @@ scala> NLong(1) val res12: NLong = NLong@1 scala> res12.toString -val res13: String = NLong@1 +val res13: String = "rs$line$17$NLong@1" scala> 1L -val res14: Long = 1 +val res14: Long = 1L scala> class NFloat(val x: Float) extends AnyVal // defined class NFloat @@ -65,7 +65,7 @@ scala> NFloat(1L) val res15: NFloat = NFloat@3f800000 scala> res15.toString -val res16: String = NFloat@3f800000 +val res16: String = "rs$line$21$NFloat@3f800000" scala> 1.0F val res17: Float = 1.0F @@ -77,7 +77,7 @@ scala> NDouble(1D) val res18: NDouble = NDouble@3ff00000 scala> res18.toString -val res19: String = NDouble@3ff00000 +val res19: String = "rs$line$25$NDouble@3ff00000" scala> 1.0D val res20: Double = 1.0 @@ -89,7 +89,7 @@ scala> NChar('a') val res21: NChar = NChar@61 scala> res21.toString -val res22: String = NChar@61 +val res22: String = "rs$line$29$NChar@61" scala> 'a' val res23: Char = 'a' @@ -101,7 +101,7 @@ scala> NString("test") val res24: NString = NString@364492 scala> res24.toString -val res25: String = NString@364492 +val res25: String = "rs$line$33$NString@364492" scala> "test" val res26: String = "test" @@ -113,7 +113,7 @@ scala> CustomToString(23) val res27: CustomToString = Test23 scala> res27.toString -val res28: String = Test23 +val res28: String = "Test23" scala> class `<>`(x: Int) extends AnyVal // defined class <> @@ -122,7 +122,7 @@ scala> `<>`(23) val res29: <> = less$greater@17 scala> res29.toString -val res30: String = less$greater@17 +val res30: String = "rs$line$40$less$greater@17" scala> class `🤪`(x: Int) extends AnyVal // defined class 🤪 @@ -131,7 +131,7 @@ scala> `🤪`(23) val res31: 🤪 = uD83E$uDD2A@17 scala> res31.toString -val res32: String = uD83E$uDD2A@17 +val res32: String = "rs$line$43$uD83E$uDD2A@17" scala> object Outer { class Foo(x: Int) extends AnyVal } // defined object Outer @@ -140,7 +140,7 @@ scala> Outer.Foo(23) val res33: Outer.Foo = Outer$Foo@17 scala> res33.toString -val res34: String = Outer$Foo@17 +val res34: String = "rs$line$46$Outer$Foo@17" scala> Vector.unapplySeq(Vector(2)) val res35: scala.collection.SeqFactory.UnapplySeqWrapper[Int] = scala.collection.SeqFactory$UnapplySeqWrapper@df507bfd diff --git a/compiler/test-resources/repl/settings-repl-max-print-both-truncation-settings b/compiler/test-resources/repl/settings-repl-max-print-both-truncation-settings index a7f7d6c10dd6..fc49e92756c4 100644 --- a/compiler/test-resources/repl/settings-repl-max-print-both-truncation-settings +++ b/compiler/test-resources/repl/settings-repl-max-print-both-truncation-settings @@ -6,5 +6,5 @@ scala>:settings -Vrepl-max-print-elements:2 scala>:settings -Vrepl-max-print-characters:50 scala> Seq(1,2,3) -val res1: Seq[Int] = List(1, 2) ... large output truncated, print value to show all +val res1: Seq[Int] = List(1, 2, 3) diff --git a/compiler/test-resources/repl/settings-repl-max-print-characters b/compiler/test-resources/repl/settings-repl-max-print-characters index 9263680b95cc..a549c924bd9e 100644 --- a/compiler/test-resources/repl/settings-repl-max-print-characters +++ b/compiler/test-resources/repl/settings-repl-max-print-characters @@ -1,7 +1,7 @@ scala> 1.to(10).mkString -val res0: String = 12345678910 +val res0: String = "12345678910" scala>:settings -Vrepl-max-print-characters:10 scala> 1.to(10).mkString -val res1: String = 123456789 ... large output truncated, print value to show all +val res1: String = "12345678910" diff --git a/compiler/test-resources/repl/settings-repl-max-print-elements b/compiler/test-resources/repl/settings-repl-max-print-elements index b203e689f020..8afb244e1cf9 100644 --- a/compiler/test-resources/repl/settings-repl-max-print-elements +++ b/compiler/test-resources/repl/settings-repl-max-print-elements @@ -4,4 +4,5 @@ val res0: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 scala>:settings -Vrepl-max-print-elements:20 scala> 1.to(300).toList -val res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20) ... large output truncated, print value to show all +val res1: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300) + From 0e78df2be20fc295a555695bf3f37a70ae71916c Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 1 Sep 2025 20:18:24 +0800 Subject: [PATCH 25/35] . --- compiler/test-resources/repl/defaultClassloader | 2 +- compiler/test-resources/repl/i15493 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/test-resources/repl/defaultClassloader b/compiler/test-resources/repl/defaultClassloader index bd9a955409b9..df2c5b40d05b 100644 --- a/compiler/test-resources/repl/defaultClassloader +++ b/compiler/test-resources/repl/defaultClassloader @@ -1,2 +1,2 @@ scala> val d: Long = (new java.sql.Date(100L)).getTime -val d: Long = 100 +val d: Long = 100L diff --git a/compiler/test-resources/repl/i15493 b/compiler/test-resources/repl/i15493 index d2e001e24592..060b5c84597d 100644 --- a/compiler/test-resources/repl/i15493 +++ b/compiler/test-resources/repl/i15493 @@ -122,7 +122,7 @@ scala> `<>`(23) val res29: <> = less$greater@17 scala> res29.toString -val res30: String = "rs$line$40$less$greater@17" +val res30: String = "rs$line$40$$less$greater@17" scala> class `🤪`(x: Int) extends AnyVal // defined class 🤪 @@ -131,7 +131,7 @@ scala> `🤪`(23) val res31: 🤪 = uD83E$uDD2A@17 scala> res31.toString -val res32: String = "rs$line$43$uD83E$uDD2A@17" +val res32: String = "rs$line$43$$uD83E$uDD2A@17" scala> object Outer { class Foo(x: Int) extends AnyVal } // defined object Outer From 7118ce2cec59d3f37d18582b38ada81d20407b4e Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Mon, 8 Sep 2025 18:57:51 +0800 Subject: [PATCH 26/35] . --- project/Build.scala | 167 ++++++++++++++++++++++++-------------------- 1 file changed, 92 insertions(+), 75 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index aa20248442be..c8346e04ee98 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -676,92 +676,109 @@ object Build { } val shadedSourceGenerator = (Compile / sourceGenerators) += Def.task { + val s = streams.value + val cacheDir = s.cacheDirectory + val dest = (Compile / sourceManaged).value / "downloaded" + val downloads = Seq( "https://repo1.maven.org/maven2/com/lihaoyi/pprint_3/0.9.3/pprint_3-0.9.3-sources.jar", "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.4/sourcecode_3-0.4.4-sources.jar", ) - val dest = ((Compile / sourceManaged).value / "downloaded").toPath - if (Files.exists(dest)) { - Files.walk(dest) - .sorted(java.util.Comparator.reverseOrder()) // delete children before parents - .forEach(p => Files.delete(p)); + + // Create a marker file that tracks the download URLs for cache invalidation + val markerFile = cacheDir / "shaded-sources-marker" + val markerContent = downloads.mkString("\n") + if (!markerFile.exists || IO.read(markerFile) != markerContent) { + IO.write(markerFile, markerContent) } - Files.createDirectories(dest) - - for(url <- downloads) { - import java.io._ - import java.net.{HttpURLConnection, URL} - import java.nio.file._ - import java.nio.file.attribute.FileTime - import java.util.zip.{ZipEntry, ZipInputStream} - - val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] - conn.setInstanceFollowRedirects(true) - conn.setConnectTimeout(15000) - conn.setReadTimeout(60000) - conn.setRequestMethod("GET") - - var in: InputStream = null - var zis: ZipInputStream = null - try { - in = new BufferedInputStream(conn.getInputStream) - zis = new ZipInputStream(in) - - var entry: ZipEntry = zis.getNextEntry - val buffer = new Array[Byte](8192) - - while (entry != null) { - val target = dest.resolve(entry.getName).normalize() - if (entry.isDirectory) Files.createDirectories(target) - else { - Files.createDirectories(target.getParent) - var out: OutputStream = null - try { - out = new BufferedOutputStream(Files.newOutputStream(target, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) - var n = zis.read(buffer) - while (n != -1) { - out.write(buffer, 0, n) - n = zis.read(buffer) - } - } finally if (out != null) out.close() - } - zis.closeEntry() - entry = zis.getNextEntry + FileFunction.cached(cacheDir / "fetchShadedSources", + FilesInfo.lastModified, FilesInfo.exists) { _ => + s.log.info(s"Downloading and processing shaded sources to $dest...") + + val destPath = dest.toPath + if (Files.exists(destPath)) { + Files.walk(destPath) + .sorted(java.util.Comparator.reverseOrder()) // delete children before parents + .forEach(p => Files.delete(p)); + } + Files.createDirectories(destPath) + + for(url <- downloads) { + import java.io._ + import java.net.{HttpURLConnection, URL} + import java.nio.file._ + import java.nio.file.attribute.FileTime + import java.util.zip.{ZipEntry, ZipInputStream} + + val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] + conn.setInstanceFollowRedirects(true) + conn.setConnectTimeout(15000) + conn.setReadTimeout(60000) + conn.setRequestMethod("GET") + + var in: InputStream = null + var zis: ZipInputStream = null + try { + in = new BufferedInputStream(conn.getInputStream) + zis = new ZipInputStream(in) + + var entry: ZipEntry = zis.getNextEntry + val buffer = new Array[Byte](8192) + + while (entry != null) { + val target = destPath.resolve(entry.getName).normalize() + if (entry.isDirectory) Files.createDirectories(target) + else { + Files.createDirectories(target.getParent) + var out: OutputStream = null + try { + out = new BufferedOutputStream(Files.newOutputStream(target, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) + var n = zis.read(buffer) + while (n != -1) { + out.write(buffer, 0, n) + n = zis.read(buffer) + } + } finally if (out != null) out.close() + } + + zis.closeEntry() + entry = zis.getNextEntry + } + } finally { + if (zis != null) zis.close() + if (in != null) in.close() + conn.disconnect() } - } finally { - if (zis != null) zis.close() - if (in != null) in.close() - conn.disconnect() } - } - import collection.JavaConverters._ - Files.walk(dest) - .filter(p => p.toString().endsWith(".scala")) - .map[java.io.File] { (file: java.nio.file.Path) => - val text = new String(Files.readAllBytes(file.path), java.nio.charset.StandardCharsets.UTF_8) - if (!file.getFileName().toString().equals("CollectionName.scala")) Files.write( - file, - ("package dotty.shaded\n" + - text - .replace("import scala", "import _root_.scala") - .replace(" scala.collection.", " _root_.scala.collection.") - .replace("_root_.pprint", "_root_.dotty.shaded.pprint") - .replace("_root_.fansi", "_root_.dotty.shaded.fansi") - .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") - .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") - .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") - .replace("head.next()", "head.nn.next()") - .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") - .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") - .replace("x.toString match{", "scala.runtime.ScalaRunTime.stringOf(x) match{")).getBytes - ) - file.toFile + import collection.JavaConverters._ + Files.walk(destPath) + .filter(p => p.toString().endsWith(".scala")) + .map[java.io.File] { (file: java.nio.file.Path) => + val text = new String(Files.readAllBytes(file), java.nio.charset.StandardCharsets.UTF_8) + if (!file.getFileName().toString().equals("CollectionName.scala")) Files.write( + file, + ("package dotty.shaded\n" + + text + .replace("import scala", "import _root_.scala") + .replace(" scala.collection.", " _root_.scala.collection.") + .replace("_root_.pprint", "_root_.dotty.shaded.pprint") + .replace("_root_.fansi", "_root_.dotty.shaded.fansi") + .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") + .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") + .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") + .replace("head.next()", "head.nn.next()") + .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") + .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") + .replace("x.toString match{", "scala.runtime.ScalaRunTime.stringOf(x) match{")).getBytes + ) + file.toFile - } - .collect(java.util.stream.Collectors.toList()).asScala.toSeq + } + .collect(java.util.stream.Collectors.toList()).asScala.toSet + } (Set(markerFile)).toSeq }.taskValue // Settings shared between scala3-compiler and scala3-compiler-bootstrapped From 94e599a971e1a57c88b5b11ca8a0a272587106bb Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 9 Sep 2025 16:55:06 +0800 Subject: [PATCH 27/35] . --- project/Build.scala | 111 ++++++++++++++------------------------------ 1 file changed, 36 insertions(+), 75 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index c8346e04ee98..36c36327ea3d 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -697,87 +697,48 @@ object Build { FilesInfo.lastModified, FilesInfo.exists) { _ => s.log.info(s"Downloading and processing shaded sources to $dest...") - val destPath = dest.toPath - if (Files.exists(destPath)) { - Files.walk(destPath) - .sorted(java.util.Comparator.reverseOrder()) // delete children before parents - .forEach(p => Files.delete(p)); + if (dest.exists) { + IO.delete(dest) } - Files.createDirectories(destPath) + IO.createDirectory(dest) for(url <- downloads) { - import java.io._ - import java.net.{HttpURLConnection, URL} - import java.nio.file._ - import java.nio.file.attribute.FileTime - import java.util.zip.{ZipEntry, ZipInputStream} - - val conn = new URL(url).openConnection().asInstanceOf[HttpURLConnection] - conn.setInstanceFollowRedirects(true) - conn.setConnectTimeout(15000) - conn.setReadTimeout(60000) - conn.setRequestMethod("GET") - - var in: InputStream = null - var zis: ZipInputStream = null - try { - in = new BufferedInputStream(conn.getInputStream) - zis = new ZipInputStream(in) - - var entry: ZipEntry = zis.getNextEntry - val buffer = new Array[Byte](8192) - - while (entry != null) { - val target = destPath.resolve(entry.getName).normalize() - if (entry.isDirectory) Files.createDirectories(target) - else { - Files.createDirectories(target.getParent) - var out: OutputStream = null - try { - out = new BufferedOutputStream(Files.newOutputStream(target, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) - var n = zis.read(buffer) - while (n != -1) { - out.write(buffer, 0, n) - n = zis.read(buffer) - } - } finally if (out != null) out.close() - } + import java.net.URL - zis.closeEntry() - entry = zis.getNextEntry - } - } finally { - if (zis != null) zis.close() - if (in != null) in.close() - conn.disconnect() - } - } + // Download jar to a temporary file + val jarName = url.substring(url.lastIndexOf('/') + 1) + val tempJar = cacheDir / jarName - import collection.JavaConverters._ - Files.walk(destPath) - .filter(p => p.toString().endsWith(".scala")) - .map[java.io.File] { (file: java.nio.file.Path) => - val text = new String(Files.readAllBytes(file), java.nio.charset.StandardCharsets.UTF_8) - if (!file.getFileName().toString().equals("CollectionName.scala")) Files.write( - file, - ("package dotty.shaded\n" + - text - .replace("import scala", "import _root_.scala") - .replace(" scala.collection.", " _root_.scala.collection.") - .replace("_root_.pprint", "_root_.dotty.shaded.pprint") - .replace("_root_.fansi", "_root_.dotty.shaded.fansi") - .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") - .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") - .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") - .replace("head.next()", "head.nn.next()") - .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") - .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") - .replace("x.toString match{", "scala.runtime.ScalaRunTime.stringOf(x) match{")).getBytes - ) - file.toFile + s.log.info(s"Downloading $jarName...") + IO.transfer(new URL(url).openStream(), tempJar) - } - .collect(java.util.stream.Collectors.toList()).asScala.toSet + // Extract the jar using SBT's IO.unzip + s.log.info(s"Extracting $jarName...") + IO.unzip(tempJar, dest) + } + + val scalaFiles = (dest ** "*.scala").get + scalaFiles.foreach { file => + val text = IO.read(file) + if (!file.getName.equals("CollectionName.scala")) { + val processedText = "package dotty.shaded\n" + + text + .replace("import scala", "import _root_.scala") + .replace(" scala.collection.", " _root_.scala.collection.") + .replace("_root_.pprint", "_root_.dotty.shaded.pprint") + .replace("_root_.fansi", "_root_.dotty.shaded.fansi") + .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") + .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") + .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") + .replace("head.next()", "head.nn.next()") + .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") + .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") + .replace("x.toString match{", "scala.runtime.ScalaRunTime.stringOf(x) match{") + + IO.write(file, processedText) + } + } + scalaFiles.toSet } (Set(markerFile)).toSeq }.taskValue From 22aa06cc570c08fc99880e515f2d896d20fedfd6 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Tue, 9 Sep 2025 20:53:36 +0800 Subject: [PATCH 28/35] . --- project/Build.scala | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 36c36327ea3d..c184058e4217 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -679,16 +679,17 @@ object Build { val s = streams.value val cacheDir = s.cacheDirectory val dest = (Compile / sourceManaged).value / "downloaded" + val lm = dependencyResolution.value - val downloads = Seq( - "https://repo1.maven.org/maven2/com/lihaoyi/pprint_3/0.9.3/pprint_3-0.9.3-sources.jar", - "https://repo1.maven.org/maven2/com/lihaoyi/fansi_3/0.5.1/fansi_3-0.5.1-sources.jar", - "https://repo1.maven.org/maven2/com/lihaoyi/sourcecode_3/0.4.4/sourcecode_3-0.4.4-sources.jar", + val dependencies = Seq( + ("com.lihaoyi", "pprint_3", "0.9.3"), + ("com.lihaoyi", "fansi_3", "0.5.1"), + ("com.lihaoyi", "sourcecode_3", "0.4.4"), ) - // Create a marker file that tracks the download URLs for cache invalidation + // Create a marker file that tracks the dependencies for cache invalidation val markerFile = cacheDir / "shaded-sources-marker" - val markerContent = downloads.mkString("\n") + val markerContent = dependencies.map { case (org, name, version) => s"$org:$name:$version:sources" }.mkString("\n") if (!markerFile.exists || IO.read(markerFile) != markerContent) { IO.write(markerFile, markerContent) } @@ -702,19 +703,25 @@ object Build { } IO.createDirectory(dest) - for(url <- downloads) { - import java.net.URL - - // Download jar to a temporary file - val jarName = url.substring(url.lastIndexOf('/') + 1) - val tempJar = cacheDir / jarName - - s.log.info(s"Downloading $jarName...") - IO.transfer(new URL(url).openStream(), tempJar) + for((org, name, version) <- dependencies) { + import sbt.librarymanagement._ + + // Retrieve sources jar using dependencyResolution + val moduleId = ModuleID(org, name, version).sources() + val retrieveDir = cacheDir / "retrieved" / s"$org-$name-$version-sources" + + s.log.info(s"Retrieving $org:$name:$version:sources...") + val retrieved = lm.retrieve(moduleId, scalaModuleInfo = None, retrieveDir, s.log) + val jarFiles = retrieved.fold( + w => throw w.resolveException, + files => files.filter(_.getName.contains("-sources.jar")) + ) - // Extract the jar using SBT's IO.unzip - s.log.info(s"Extracting $jarName...") - IO.unzip(tempJar, dest) + // Extract each retrieved jar + jarFiles.foreach { jarFile => + s.log.info(s"Extracting ${jarFile.getName}...") + IO.unzip(jarFile, dest) + } } val scalaFiles = (dest ** "*.scala").get From f5269513789c70facfe8cf85038baa139d76e42f Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 10:02:32 +0800 Subject: [PATCH 29/35] . --- project/Build.scala | 80 ++------------------------------------------- 1 file changed, 3 insertions(+), 77 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index c184058e4217..af6517111fae 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -675,80 +675,6 @@ object Build { recur(lines) } - val shadedSourceGenerator = (Compile / sourceGenerators) += Def.task { - val s = streams.value - val cacheDir = s.cacheDirectory - val dest = (Compile / sourceManaged).value / "downloaded" - val lm = dependencyResolution.value - - val dependencies = Seq( - ("com.lihaoyi", "pprint_3", "0.9.3"), - ("com.lihaoyi", "fansi_3", "0.5.1"), - ("com.lihaoyi", "sourcecode_3", "0.4.4"), - ) - - // Create a marker file that tracks the dependencies for cache invalidation - val markerFile = cacheDir / "shaded-sources-marker" - val markerContent = dependencies.map { case (org, name, version) => s"$org:$name:$version:sources" }.mkString("\n") - if (!markerFile.exists || IO.read(markerFile) != markerContent) { - IO.write(markerFile, markerContent) - } - - FileFunction.cached(cacheDir / "fetchShadedSources", - FilesInfo.lastModified, FilesInfo.exists) { _ => - s.log.info(s"Downloading and processing shaded sources to $dest...") - - if (dest.exists) { - IO.delete(dest) - } - IO.createDirectory(dest) - - for((org, name, version) <- dependencies) { - import sbt.librarymanagement._ - - // Retrieve sources jar using dependencyResolution - val moduleId = ModuleID(org, name, version).sources() - val retrieveDir = cacheDir / "retrieved" / s"$org-$name-$version-sources" - - s.log.info(s"Retrieving $org:$name:$version:sources...") - val retrieved = lm.retrieve(moduleId, scalaModuleInfo = None, retrieveDir, s.log) - val jarFiles = retrieved.fold( - w => throw w.resolveException, - files => files.filter(_.getName.contains("-sources.jar")) - ) - - // Extract each retrieved jar - jarFiles.foreach { jarFile => - s.log.info(s"Extracting ${jarFile.getName}...") - IO.unzip(jarFile, dest) - } - } - - val scalaFiles = (dest ** "*.scala").get - scalaFiles.foreach { file => - val text = IO.read(file) - if (!file.getName.equals("CollectionName.scala")) { - val processedText = "package dotty.shaded\n" + - text - .replace("import scala", "import _root_.scala") - .replace(" scala.collection.", " _root_.scala.collection.") - .replace("_root_.pprint", "_root_.dotty.shaded.pprint") - .replace("_root_.fansi", "_root_.dotty.shaded.fansi") - .replace("def apply(c: Char): Trie[T]", "def apply(c: Char): Trie[T] | Null") - .replace("var head: Iterator[T] = null", "var head: Iterator[T] | Null = null") - .replace("if (head != null && head.hasNext) true", "if (head != null && head.nn.hasNext) true") - .replace("head.next()", "head.nn.next()") - .replace("abstract class Walker", "@scala.annotation.nowarn abstract class Walker") - .replace("object TPrintLowPri", "@scala.annotation.nowarn object TPrintLowPri") - .replace("x.toString match{", "scala.runtime.ScalaRunTime.stringOf(x) match{") - - IO.write(file, processedText) - } - } - scalaFiles.toSet - } (Set(markerFile)).toSeq - - }.taskValue // Settings shared between scala3-compiler and scala3-compiler-bootstrapped lazy val commonDottyCompilerSettings = Seq( // Note: bench/profiles/projects.yml should be updated accordingly. @@ -795,7 +721,7 @@ object Build { ("io.get-coursier" %% "coursier" % "2.0.16" % Test).cross(CrossVersion.for3Use2_13), ), - shadedSourceGenerator, + (Compile / sourceGenerators) += ShadedSourceGenerator.task.taskValue, // For convenience, change the baseDirectory when running the compiler Compile / forkOptions := (Compile / forkOptions).value.withWorkingDirectory((ThisBuild / baseDirectory).value), @@ -2217,7 +2143,7 @@ object Build { Seq(file) }.taskValue, - shadedSourceGenerator, + (Compile / sourceGenerators) += ShadedSourceGenerator.task.taskValue, // sbt adds all the projects to scala-tool config which breaks building the scalaInstance // as a workaround, I build it manually by only adding the compiler scalaInstance := { @@ -2407,7 +2333,7 @@ object Build { sjsSources } (Set(scalaJSIRSourcesJar)).toSeq }.taskValue, - shadedSourceGenerator + (Compile / sourceGenerators) += ShadedSourceGenerator.task.taskValue ) // ============================================================================================== From 2e7fef6e6c92d52920a732a39d9c4253679f2ffb Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 10:07:22 +0800 Subject: [PATCH 30/35] . --- project/ShadedSourceGenerator.scala | 112 ++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 project/ShadedSourceGenerator.scala diff --git a/project/ShadedSourceGenerator.scala b/project/ShadedSourceGenerator.scala new file mode 100644 index 000000000000..345e67ebfbb9 --- /dev/null +++ b/project/ShadedSourceGenerator.scala @@ -0,0 +1,112 @@ +import sbt._ +import sbt.Keys._ + +/** + * ShadedSourceGenerator - A build plugin for creating shaded versions of external dependencies + * + * This generator downloads source JARs for specified dependencies (currently pprint, fansi, and sourcecode), + * extracts them, and applies patches to: + * 1. Add the dotty.shaded package prefix + * 2. Rewrite imports to use _root_ to avoid conflicts + * 3. Apply Scala 3 compatibility fixes (mostly due to enforcing null safety in scala/scala3) + * + * The shaded sources are placed in the managed source directory and included in compilation. + * This allows the Scala 3 compiler to bundle these utilities without external dependencies. + */ +object ShadedSourceGenerator { + + val task = Def.task { + val s = streams.value + val cacheDir = s.cacheDirectory + val dest = (Compile / sourceManaged).value / "downloaded" + val lm = dependencyResolution.value + + val dependencies = Seq( + ("com.lihaoyi", "pprint_3", "0.9.3"), + ("com.lihaoyi", "fansi_3", "0.5.1"), + ("com.lihaoyi", "sourcecode_3", "0.4.4"), + ) + + // Create a marker file that tracks the dependencies for cache invalidation + val markerFile = cacheDir / "shaded-sources-marker" + val markerContent = dependencies.map { case (org, name, version) => s"$org:$name:$version:sources" }.mkString("\n") + if (!markerFile.exists || IO.read(markerFile) != markerContent) { + IO.write(markerFile, markerContent) + } + + FileFunction.cached(cacheDir / "fetchShadedSources", + FilesInfo.lastModified, FilesInfo.exists) { _ => + s.log.info(s"Downloading and processing shaded sources to $dest...") + + if (dest.exists) { + IO.delete(dest) + } + IO.createDirectory(dest) + + for((org, name, version) <- dependencies) { + import sbt.librarymanagement._ + + val moduleId = ModuleID(org, name, version).sources() + val retrieveDir = cacheDir / "retrieved" / s"$org-$name-$version-sources" + + s.log.info(s"Retrieving $org:$name:$version:sources...") + val retrieved = lm.retrieve(moduleId, scalaModuleInfo = None, retrieveDir, s.log) + val jarFiles = retrieved.fold( + w => throw w.resolveException, + files => files.filter(_.getName.contains("-sources.jar")) + ) + + jarFiles.foreach { jarFile => + s.log.info(s"Extracting ${jarFile.getName}...") + IO.unzip(jarFile, dest) + } + } + + val scalaFiles = (dest ** "*.scala").get + + // Define patches as a map from search text to replacement text + val patches = Map( + "import scala" -> "import _root_.scala", + " scala.collection." -> " _root_.scala.collection.", + "_root_.pprint" -> "_root_.dotty.shaded.pprint", + "_root_.fansi" -> "_root_.dotty.shaded.fansi", + "def apply(c: Char): Trie[T]" -> "def apply(c: Char): Trie[T] | Null", + "var head: Iterator[T] = null" -> "var head: Iterator[T] | Null = null", + "if (head != null && head.hasNext) true" -> "if (head != null && head.nn.hasNext) true", + "head.next()" -> "head.nn.next()", + "abstract class Walker" -> "@scala.annotation.nowarn abstract class Walker", + "object TPrintLowPri" -> "@scala.annotation.nowarn object TPrintLowPri", + "x.toString match{" -> "scala.runtime.ScalaRunTime.stringOf(x) match{" + ) + + val patchUsageCounter = scala.collection.mutable.Map(patches.keys.map(_ -> 0).toSeq: _*) + + scalaFiles.foreach { file => + val text = IO.read(file) + if (!file.getName.equals("CollectionName.scala")) { + var processedText = "package dotty.shaded\n" + text + + // Apply patches and count usage + patches.foreach { case (search, replacement) => + if (processedText.contains(search)) { + processedText = processedText.replace(search, replacement) + patchUsageCounter(search) += 1 + } + } + + IO.write(file, processedText) + } + } + + // Assert that all patches were applied at least once + val unappliedPatches = patchUsageCounter.filter(_._2 == 0).keys + if (unappliedPatches.nonEmpty) { + throw new RuntimeException(s"Patches were not applied: ${unappliedPatches.mkString(", ")}") + } + + scalaFiles.toSet + } (Set(markerFile)).toSeq + + } + +} \ No newline at end of file From 49f57269042d4d1a9a827375e2540b4a2040600a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 10:12:34 +0800 Subject: [PATCH 31/35] . --- compiler/src/dotty/tools/repl/ReplDriver.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 0b67c36492ae..a191c7e1d5b1 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -122,9 +122,11 @@ class ReplDriver(settings: Array[String], final def initialState: State = val emptyState = State(0, 0, Map.empty, Set.empty, false, rootCtx) val initScript = rootCtx.settings.replInitScript.value(using rootCtx) - initScript.trim() match - case "" => emptyState - case script => run(script)(using emptyState) + val pprintImport = "import dotty.shaded.pprint.pprintln" + val combinedScript = initScript.trim() match + case "" => pprintImport + case script => s"$pprintImport\n$script" + run(combinedScript)(using emptyState) /** Reset state of repl to the initial state * From 2fda5e18f1776231eefb348cdc96b5b8b09dbaf7 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 10:15:25 +0800 Subject: [PATCH 32/35] . --- project/ShadedSourceGenerator.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/project/ShadedSourceGenerator.scala b/project/ShadedSourceGenerator.scala index 345e67ebfbb9..e3a1c9680db9 100644 --- a/project/ShadedSourceGenerator.scala +++ b/project/ShadedSourceGenerator.scala @@ -68,8 +68,6 @@ object ShadedSourceGenerator { val patches = Map( "import scala" -> "import _root_.scala", " scala.collection." -> " _root_.scala.collection.", - "_root_.pprint" -> "_root_.dotty.shaded.pprint", - "_root_.fansi" -> "_root_.dotty.shaded.fansi", "def apply(c: Char): Trie[T]" -> "def apply(c: Char): Trie[T] | Null", "var head: Iterator[T] = null" -> "var head: Iterator[T] | Null = null", "if (head != null && head.hasNext) true" -> "if (head != null && head.nn.hasNext) true", From 4d1139c6182d0c9d919aa783e0ed3f6efc3bb6eb Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 10:23:07 +0800 Subject: [PATCH 33/35] . --- compiler/src/dotty/tools/repl/Rendering.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index bf2af3ed0823..4242fd3616eb 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -54,7 +54,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - dotty.shaded.pprint.PPrinter.BlackWhite.apply(value).plainText + dotty.shaded.pprint.PPrinter.BlackWhite.apply(value, height = 50, width = 100).plainText /** Load the value of the symbol using reflection. * From 38831b76884e5e8bc8288f8c77b277233dc9e27d Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 11:06:15 +0800 Subject: [PATCH 34/35] . --- compiler/src/dotty/tools/repl/Main.scala | 2 +- compiler/src/dotty/tools/repl/Rendering.scala | 6 ++++-- compiler/src/dotty/tools/repl/ReplDriver.scala | 10 ++++++---- .../tools/languageserver/worksheet/ReplProcess.scala | 2 +- sbt-bridge/src/xsbt/ConsoleInterface.java | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Main.scala b/compiler/src/dotty/tools/repl/Main.scala index 7eb906edc586..f4d6b505d430 100644 --- a/compiler/src/dotty/tools/repl/Main.scala +++ b/compiler/src/dotty/tools/repl/Main.scala @@ -4,5 +4,5 @@ package dotty.tools.repl // To test, run bin/scala object Main { def main(args: Array[String]): Unit = - new ReplDriver(args).tryRunning + new ReplDriver(args, extraPredef = ReplDriver.pprintImport).tryRunning } diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 4242fd3616eb..238353867516 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -53,8 +53,10 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): else str.substring(0, str.offsetByCodePoints(0, maxPrintCharacters - 1)) /** Return a String representation of a value we got from `classLoader()`. */ - private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = - dotty.shaded.pprint.PPrinter.BlackWhite.apply(value, height = 50, width = 100).plainText + private[repl] def replStringOf(sym: Symbol, value: Object)(using Context): String = { + // pretty-print things with 100 cols 50 rows by default, + dotty.shaded.pprint.PPrinter.BlackWhite.apply(value, width = 100, height = 50).plainText + } /** Load the value of the symbol using reflection. * diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index a191c7e1d5b1..befb3de9a941 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -77,7 +77,8 @@ case class State(objectIndex: Int, /** Main REPL instance, orchestrating input, compilation and presentation */ class ReplDriver(settings: Array[String], out: PrintStream = Console.out, - classLoader: Option[ClassLoader] = None) extends Driver: + classLoader: Option[ClassLoader] = None, + extraPredef: String = "") extends Driver: /** Overridden to `false` in order to not have to give sources on the * commandline @@ -122,10 +123,9 @@ class ReplDriver(settings: Array[String], final def initialState: State = val emptyState = State(0, 0, Map.empty, Set.empty, false, rootCtx) val initScript = rootCtx.settings.replInitScript.value(using rootCtx) - val pprintImport = "import dotty.shaded.pprint.pprintln" val combinedScript = initScript.trim() match - case "" => pprintImport - case script => s"$pprintImport\n$script" + case "" => extraPredef + case script => s"$extraPredef\n$script" run(combinedScript)(using emptyState) /** Reset state of repl to the initial state @@ -640,3 +640,5 @@ class ReplDriver(settings: Array[String], case _ => ReplConsoleReporter.doReport(dia)(using state.context) end ReplDriver +object ReplDriver: + def pprintImport = "import dotty.shaded.pprint.pprintln\n" \ No newline at end of file diff --git a/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala b/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala index e8c02744397a..111863dad5e1 100644 --- a/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala +++ b/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala @@ -4,7 +4,7 @@ import dotty.tools.repl.ReplDriver object ReplProcess { def main(args: Array[String]): Unit = { - val driver = new ReplDriver(args) + val driver = new ReplDriver(args, extraPredef = ReplDriver.pprintImport) val in = new InputStreamConsumer(System.in) var state = driver.initialState diff --git a/sbt-bridge/src/xsbt/ConsoleInterface.java b/sbt-bridge/src/xsbt/ConsoleInterface.java index 3467e6abc226..49194fdf53d2 100644 --- a/sbt-bridge/src/xsbt/ConsoleInterface.java +++ b/sbt-bridge/src/xsbt/ConsoleInterface.java @@ -40,7 +40,7 @@ public void run( completeArgsList.add(classpathString); String[] completeArgs = completeArgsList.toArray(args); - ReplDriver driver = new ReplDriver(completeArgs, System.out, Some.apply(loader)); + ReplDriver driver = new ReplDriver(completeArgs, System.out, Some.apply(loader), ReplDriver.pprintImport()); State state = driver.initialState(); assert bindNames.length == bindValues.length; From 347bdd93d5ce0d504c10fc030484bfb1e3db74ce Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Thu, 11 Sep 2025 11:38:08 +0800 Subject: [PATCH 35/35] . --- .../src/dotty/tools/languageserver/worksheet/ReplProcess.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala b/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala index 111863dad5e1..e8c02744397a 100644 --- a/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala +++ b/language-server/src/dotty/tools/languageserver/worksheet/ReplProcess.scala @@ -4,7 +4,7 @@ import dotty.tools.repl.ReplDriver object ReplProcess { def main(args: Array[String]): Unit = { - val driver = new ReplDriver(args, extraPredef = ReplDriver.pprintImport) + val driver = new ReplDriver(args) val in = new InputStreamConsumer(System.in) var state = driver.initialState