From 60f9f45c4296aeb8fcfebc758091c5f7628fea48 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 8 Sep 2022 16:08:53 +0200 Subject: [PATCH 01/14] REPL: make truncation behaviour configurable via `-Xrepl-max-print-elements`. The default behaviour is unchanged: ``` scala> 1.to(300).toList val res0: 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, 22 ... large output truncated, print value to show all ``` The user can now configure a higher threshold before truncating kicks in: ``` scala -Xrepl-max-print-elements:2000 scala> 1.to(300).toList // prints the entire list ``` --- .../dotty/tools/dotc/config/ScalaSettings.scala | 1 + compiler/src/dotty/tools/repl/Rendering.scala | 14 ++++++-------- compiler/src/dotty/tools/repl/ReplDriver.scala | 6 +++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 9e34f8d726b5..196784cdcbfb 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -231,6 +231,7 @@ private sealed trait XSettings: val XprintSuspension: Setting[Boolean] = BooleanSetting("-Xprint-suspension", "Show when code is suspended until macros are compiled.") val Xprompt: Setting[Boolean] = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).") val XreplDisableDisplay: Setting[Boolean] = BooleanSetting("-Xrepl-disable-display", "Do not display definitions in REPL.") + val XreplMaxPrintElements: Setting[Int] = IntSetting("-Xrepl-max-print-elements", "Number of elements to be printed before output is truncated.", 1000) val XverifySignatures: Setting[Boolean] = BooleanSetting("-Xverify-signatures", "Verify generic signatures in generated bytecode.") val XignoreScala2Macros: Setting[Boolean] = BooleanSetting("-Xignore-scala2-macros", "Ignore errors when compiling code that calls Scala2 macros, these will fail at runtime.") val XimportSuggestionTimeout: Setting[Int] = IntSetting("-Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 608ca23c5fec..81f69457eb20 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -24,17 +24,15 @@ import dotc.transform.ValueClasses * `ReplDriver#resetToInitial` is called, the accompanying instance of * `Rendering` is no longer valid. */ -private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): +private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None, + maxPrintElements: Int = 1000): import Rendering._ - private val MaxStringElements: Int = 1000 // no need to mkString billions of elements - private var myClassLoader: AbstractFileClassLoader = _ private var myReplStringOf: Object => String = _ - /** Class loader used to load compiled code */ private[repl] def classLoader()(using Context) = if (myClassLoader != null && myClassLoader.root == ctx.settings.outputDir.value) myClassLoader @@ -64,12 +62,12 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int], classOf[Boolean]) val truly = java.lang.Boolean.TRUE - (value: Object) => meth.invoke(null, value, Integer.valueOf(MaxStringElements), truly).asInstanceOf[String] + (value: Object) => meth.invoke(null, value, Integer.valueOf(maxPrintElements), truly).asInstanceOf[String] } catch { case _: NoSuchMethodException => val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int]) - (value: Object) => meth.invoke(null, value, Integer.valueOf(MaxStringElements)).asInstanceOf[String] + (value: Object) => meth.invoke(null, value, Integer.valueOf(maxPrintElements)).asInstanceOf[String] } } myClassLoader @@ -84,8 +82,8 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): private[repl] def truncate(str: String): String = val showTruncated = " ... large output truncated, print value to show all" val ncp = str.codePointCount(0, str.length) // to not cut inside code point - if ncp <= MaxStringElements then str - else str.substring(0, str.offsetByCodePoints(0, MaxStringElements - 1)) + showTruncated + if ncp <= maxPrintElements then str + else str.substring(0, str.offsetByCodePoints(0, maxPrintElements - 1)) + showTruncated /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(value: Object)(using Context): String = diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 4fab4b119a08..d6964dfe09f6 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -111,8 +111,12 @@ class ReplDriver(settings: Array[String], if (rootCtx.settings.outputDir.isDefault(using rootCtx)) rootCtx = rootCtx.fresh .setSetting(rootCtx.settings.outputDir, new VirtualDirectory("")) + compiler = new ReplCompiler - rendering = new Rendering(classLoader) + rendering = new Rendering( + classLoader, + maxPrintElements = rootCtx.settings.XreplMaxPrintElements.valueIn(rootCtx.settingsState) + ) } private var rootCtx: Context = _ From 35a61a5c59b836e0481e757961a55d70de5a2e65 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Sun, 11 Sep 2022 09:09:09 +0200 Subject: [PATCH 02/14] use `-V` for the option prefix, which suggests "verbose" or "view"... ... for when you want to tweak output (as opposed to compiler behavior). as suggested in https://github.com/lampepfl/dotty/pull/16011#issuecomment-1242328332 --- compiler/src/dotty/tools/dotc/config/ScalaSettings.scala | 2 +- compiler/src/dotty/tools/repl/ReplDriver.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 196784cdcbfb..7da0f2c4e33e 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -149,6 +149,7 @@ private sealed trait VerboseSettings: val Vprofile: Setting[Boolean] = BooleanSetting("-Vprofile", "Show metrics about sources and internal representations to estimate compile-time complexity.") val VprofileSortedBy = ChoiceSetting("-Vprofile-sorted-by", "key", "Show metrics about sources and internal representations sorted by given column name", List("name", "path", "lines", "tokens", "tasty", "complexity"), "") val VprofileDetails = IntSetting("-Vprofile-details", "Show metrics about sources and internal representations of the most complex methods", 0) + val VreplMaxPrintElements: Setting[Int] = IntSetting("-Vrepl-max-print-elements", "Number of elements to be printed before output is truncated.", 1000) /** -W "Warnings" settings */ @@ -231,7 +232,6 @@ private sealed trait XSettings: val XprintSuspension: Setting[Boolean] = BooleanSetting("-Xprint-suspension", "Show when code is suspended until macros are compiled.") val Xprompt: Setting[Boolean] = BooleanSetting("-Xprompt", "Display a prompt after each error (debugging option).") val XreplDisableDisplay: Setting[Boolean] = BooleanSetting("-Xrepl-disable-display", "Do not display definitions in REPL.") - val XreplMaxPrintElements: Setting[Int] = IntSetting("-Xrepl-max-print-elements", "Number of elements to be printed before output is truncated.", 1000) val XverifySignatures: Setting[Boolean] = BooleanSetting("-Xverify-signatures", "Verify generic signatures in generated bytecode.") val XignoreScala2Macros: Setting[Boolean] = BooleanSetting("-Xignore-scala2-macros", "Ignore errors when compiling code that calls Scala2 macros, these will fail at runtime.") val XimportSuggestionTimeout: Setting[Int] = IntSetting("-Ximport-suggestion-timeout", "Timeout (in ms) for searching for import suggestions when errors are reported.", 8000) diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index d6964dfe09f6..0c480d281de5 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -115,7 +115,7 @@ class ReplDriver(settings: Array[String], compiler = new ReplCompiler rendering = new Rendering( classLoader, - maxPrintElements = rootCtx.settings.XreplMaxPrintElements.valueIn(rootCtx.settingsState) + maxPrintElements = rootCtx.settings.VreplMaxPrintElements.valueIn(rootCtx.settingsState) ) } From e48c71e633bdfa054e4fe8976085a503f66095fc Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Sun, 11 Sep 2022 09:24:57 +0200 Subject: [PATCH 03/14] ctx.settings.VreplMaxPrintElements.value at all use-sites as suggested in https://github.com/lampepfl/dotty/pull/16011#discussion_r967282830 --- compiler/src/dotty/tools/repl/Rendering.scala | 11 +++++++---- compiler/src/dotty/tools/repl/ReplDriver.scala | 5 +---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 81f69457eb20..972b68cf1169 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -24,8 +24,9 @@ import dotc.transform.ValueClasses * `ReplDriver#resetToInitial` is called, the accompanying instance of * `Rendering` is no longer valid. */ -private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None, - maxPrintElements: Int = 1000): +private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): + // val maxPrintElements: Int = 1000 + // rootCtx.settings.VreplMaxPrintElements.valueIn(rootCtx.settingsState) import Rendering._ @@ -34,7 +35,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None, private var myReplStringOf: Object => String = _ /** Class loader used to load compiled code */ - private[repl] def classLoader()(using Context) = + private[repl] def classLoader()(using ctx: Context) = if (myClassLoader != null && myClassLoader.root == ctx.settings.outputDir.value) myClassLoader else { val parent = Option(myClassLoader).orElse(parentClassLoader).getOrElse { @@ -50,6 +51,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None, } myClassLoader = new AbstractFileClassLoader(ctx.settings.outputDir.value, parent) + val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) 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 @@ -79,7 +81,8 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None, * then this bug will surface, so perhaps better not? * https://github.com/scala/bug/issues/12337 */ - private[repl] def truncate(str: String): String = + private[repl] def truncate(str: String)(using ctx: Context): String = + val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) val showTruncated = " ... large output truncated, print value to show all" val ncp = str.codePointCount(0, str.length) // to not cut inside code point if ncp <= maxPrintElements then str diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 0c480d281de5..c70d3be0740c 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -113,10 +113,7 @@ class ReplDriver(settings: Array[String], .setSetting(rootCtx.settings.outputDir, new VirtualDirectory("")) compiler = new ReplCompiler - rendering = new Rendering( - classLoader, - maxPrintElements = rootCtx.settings.VreplMaxPrintElements.valueIn(rootCtx.settingsState) - ) + rendering = new Rendering(classLoader) } private var rootCtx: Context = _ From 3e08a78452213d75e3d7f1ed89f504b5849ada7d Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Sun, 11 Sep 2022 09:32:30 +0200 Subject: [PATCH 04/14] cleanup --- compiler/src/dotty/tools/repl/Rendering.scala | 2 -- compiler/src/dotty/tools/repl/ReplDriver.scala | 1 - 2 files changed, 3 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 972b68cf1169..fe3fd77c0c24 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -25,8 +25,6 @@ import dotc.transform.ValueClasses * `Rendering` is no longer valid. */ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): - // val maxPrintElements: Int = 1000 - // rootCtx.settings.VreplMaxPrintElements.valueIn(rootCtx.settingsState) import Rendering._ diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index c70d3be0740c..4fab4b119a08 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -111,7 +111,6 @@ class ReplDriver(settings: Array[String], if (rootCtx.settings.outputDir.isDefault(using rootCtx)) rootCtx = rootCtx.fresh .setSetting(rootCtx.settings.outputDir, new VirtualDirectory("")) - compiler = new ReplCompiler rendering = new Rendering(classLoader) } From 0eb5f26278bf0fb2c9a9e8d907b0306be553e2d3 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Tue, 13 Sep 2022 10:43:46 +0200 Subject: [PATCH 05/14] add test for `-Xrepl-disable-display` --- .../repl/settings-repl-disable-display | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 compiler/test-resources/repl/settings-repl-disable-display diff --git a/compiler/test-resources/repl/settings-repl-disable-display b/compiler/test-resources/repl/settings-repl-disable-display new file mode 100644 index 000000000000..ba2c1c64574b --- /dev/null +++ b/compiler/test-resources/repl/settings-repl-disable-display @@ -0,0 +1,12 @@ +scala> 1 +val res0: Int = 1 + +scala>:settings -Xrepl-disable-display + +scala> 2 + +scala>:reset +Resetting REPL state. + +scala> 3 +val res0: Int = 3 \ No newline at end of file From a5fce184580e14e873f04c348554ac104c13a1c8 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Tue, 13 Sep 2022 10:46:47 +0200 Subject: [PATCH 06/14] add test for `-Vrepl-max-print-elements` --- .../test-resources/repl/settings-repl-max-print-elements | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 compiler/test-resources/repl/settings-repl-max-print-elements diff --git a/compiler/test-resources/repl/settings-repl-max-print-elements b/compiler/test-resources/repl/settings-repl-max-print-elements new file mode 100644 index 000000000000..d1a92a84f33d --- /dev/null +++ b/compiler/test-resources/repl/settings-repl-max-print-elements @@ -0,0 +1,7 @@ +scala> 1.to(300).toList +val res0: 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, 22 ... large output truncated, print value to show all + +scala>:settings -Vrepl-max-print-elements:2000 + +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, 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 f57fb8e2945f3f07e10b3f46dd0ec940f6e9326d Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Wed, 21 Sep 2022 14:08:16 +0200 Subject: [PATCH 07/14] naming the `using` parameter isn't required here (PR comment) --- 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 fe3fd77c0c24..f388f3009ca2 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -33,7 +33,7 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): private var myReplStringOf: Object => String = _ /** Class loader used to load compiled code */ - private[repl] def classLoader()(using ctx: Context) = + private[repl] def classLoader()(using Context) = if (myClassLoader != null && myClassLoader.root == ctx.settings.outputDir.value) myClassLoader else { val parent = Option(myClassLoader).orElse(parentClassLoader).getOrElse { From 9f5cc905a935d09f04fb31cca03336a2b9b3a25a Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Wed, 21 Sep 2022 16:26:54 +0200 Subject: [PATCH 08/14] rename variable and update comments re https://github.com/lampepfl/dotty/pull/16011/files#r976507277 --- compiler/src/dotty/tools/repl/Rendering.scala | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index f388f3009ca2..c689b55bb097 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -73,18 +73,15 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): myClassLoader } - /** Used to elide long output in replStringOf. - * - * TODO: Perhaps implement setting scala.repl.maxprintstring as in Scala 2, but - * then this bug will surface, so perhaps better not? - * https://github.com/scala/bug/issues/12337 - */ + /** Used to elide long output in replStringOf. */ private[repl] def truncate(str: String)(using ctx: Context): String = - val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) + // FIXME introduce a new setting `-Vrepl-max-print-characters` that allows to limit truncation by character properly + // see https://github.com/lampepfl/dotty/pull/16011#issuecomment-1245099657 + val maxPrintCharacters = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) val showTruncated = " ... large output truncated, print value to show all" val ncp = str.codePointCount(0, str.length) // to not cut inside code point - if ncp <= maxPrintElements then str - else str.substring(0, str.offsetByCodePoints(0, maxPrintElements - 1)) + showTruncated + if ncp <= maxPrintCharacters then str + else str.substring(0, str.offsetByCodePoints(0, maxPrintCharacters - 1)) + showTruncated /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(value: Object)(using Context): String = From fc58628feb110ce3167c781dead772e615a19496 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Wed, 28 Sep 2022 09:30:40 +0200 Subject: [PATCH 09/14] partial revert due to inconsistency with settings (pr comment) specifically, go with option 2 from this comment: https://github.com/lampepfl/dotty/pull/16011#discussion_r978775319 > add just one setting in this PR and replace the hardcoded value only in the places where it semantically corresponds to this setting and after that add the other setting in a followup PR --- compiler/src/dotty/tools/repl/Rendering.scala | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index c689b55bb097..cd32b76e0efa 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -28,6 +28,8 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): import Rendering._ + private val MaxStringElements: Int = 1000 // no need to mkString billions of elements + private var myClassLoader: AbstractFileClassLoader = _ private var myReplStringOf: Object => String = _ @@ -73,15 +75,17 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): myClassLoader } - /** Used to elide long output in replStringOf. */ - private[repl] def truncate(str: String)(using ctx: Context): String = - // FIXME introduce a new setting `-Vrepl-max-print-characters` that allows to limit truncation by character properly - // see https://github.com/lampepfl/dotty/pull/16011#issuecomment-1245099657 - val maxPrintCharacters = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) + /** Used to elide long output in replStringOf. + * + * TODO: Perhaps implement setting scala.repl.maxprintstring as in Scala 2, but + * then this bug will surface, so perhaps better not? + * https://github.com/scala/bug/issues/12337 + */ + private[repl] def truncate(str: String): String = val showTruncated = " ... large output truncated, print value to show all" val ncp = str.codePointCount(0, str.length) // to not cut inside code point - if ncp <= maxPrintCharacters then str - else str.substring(0, str.offsetByCodePoints(0, maxPrintCharacters - 1)) + showTruncated + if ncp <= MaxStringElements then str + else str.substring(0, str.offsetByCodePoints(0, MaxStringElements - 1)) + showTruncated /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(value: Object)(using Context): String = From 3e21b0b7945194f496f5fc0bdbae591b39dac53c Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Wed, 28 Sep 2022 09:40:29 +0200 Subject: [PATCH 10/14] adapt test expectation... doesn't quite work yet, the changed setting isn't picked up --- compiler/test-resources/repl/settings-repl-max-print-elements | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test-resources/repl/settings-repl-max-print-elements b/compiler/test-resources/repl/settings-repl-max-print-elements index d1a92a84f33d..710540169595 100644 --- a/compiler/test-resources/repl/settings-repl-max-print-elements +++ b/compiler/test-resources/repl/settings-repl-max-print-elements @@ -1,7 +1,7 @@ scala> 1.to(300).toList val res0: 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, 22 ... large output truncated, print value to show all -scala>:settings -Vrepl-max-print-elements:2000 +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, 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) +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) From 1904a78e8c35d3e2307f29d0a7333444065fb985 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Wed, 28 Sep 2022 17:31:59 +0200 Subject: [PATCH 11/14] fix test: pass `maxElements` into `myReplStringOf` thanks @prolativ --- compiler/src/dotty/tools/repl/Rendering.scala | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index cd32b76e0efa..cb4f00f72878 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -32,7 +32,8 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): private var myClassLoader: AbstractFileClassLoader = _ - private var myReplStringOf: Object => String = _ + /** (value, maxElements) => String */ + private var myReplStringOf: (Object, Int) => String = _ /** Class loader used to load compiled code */ private[repl] def classLoader()(using Context) = @@ -64,12 +65,12 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int], classOf[Boolean]) val truly = java.lang.Boolean.TRUE - (value: Object) => meth.invoke(null, value, Integer.valueOf(maxPrintElements), truly).asInstanceOf[String] + (value: Object, maxElements: Int) => meth.invoke(null, value, maxElements, truly).asInstanceOf[String] } catch { case _: NoSuchMethodException => val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int]) - (value: Object) => meth.invoke(null, value, Integer.valueOf(maxPrintElements)).asInstanceOf[String] + (value: Object, maxElements: Int) => meth.invoke(null, value, maxElements).asInstanceOf[String] } } myClassLoader @@ -91,7 +92,8 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): private[repl] def replStringOf(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 res = myReplStringOf(value) + val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) + val res = myReplStringOf(value, maxPrintElements) if res == null then "null // non-null reference has null-valued toString" else truncate(res) /** Load the value of the symbol using reflection. From 2da662a5e2d3a93363261cfaa8870da53a14bd2e Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 29 Sep 2022 09:21:48 +0200 Subject: [PATCH 12/14] refactor --- compiler/src/dotty/tools/repl/Rendering.scala | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index cb4f00f72878..0f69b3d470d9 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -35,6 +35,9 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): /** (value, maxElements) => String */ private var myReplStringOf: (Object, Int) => String = _ + /** info to add if output got truncated */ + private val infoOutputGotTruncated = " ... large output truncated, print value to show all" + /** Class loader used to load compiled code */ private[repl] def classLoader()(using Context) = if (myClassLoader != null && myClassLoader.root == ctx.settings.outputDir.value) myClassLoader @@ -52,7 +55,6 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): } myClassLoader = new AbstractFileClassLoader(ctx.settings.outputDir.value, parent) - val maxPrintElements = ctx.settings.VreplMaxPrintElements.valueIn(ctx.settingsState) 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 @@ -61,17 +63,19 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): // For old API, try to clean up extraneous newlines by stripping suffix and maybe prefix newline. val scalaRuntime = Class.forName("scala.runtime.ScalaRunTime", true, myClassLoader) val renderer = "stringOf" // was: replStringOf - try { - val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int], classOf[Boolean]) - val truly = java.lang.Boolean.TRUE - - (value: Object, maxElements: Int) => meth.invoke(null, value, maxElements, truly).asInstanceOf[String] - } catch { - case _: NoSuchMethodException => - val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int]) - - (value: Object, maxElements: Int) => meth.invoke(null, value, maxElements).asInstanceOf[String] + def stringOfMaybeTruncated(value: Object, maxElements: Int): String = { + try { + val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int], classOf[Boolean]) + val truly = java.lang.Boolean.TRUE + meth.invoke(null, value, maxElements, truly).asInstanceOf[String] + } catch { + case _: NoSuchMethodException => + val meth = scalaRuntime.getMethod(renderer, classOf[Object], classOf[Int]) + meth.invoke(null, value, maxElements).asInstanceOf[String] + } } + + stringOfMaybeTruncated } myClassLoader } @@ -83,10 +87,9 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): * https://github.com/scala/bug/issues/12337 */ private[repl] def truncate(str: String): String = - val showTruncated = " ... large output truncated, print value to show all" val ncp = str.codePointCount(0, str.length) // to not cut inside code point if ncp <= MaxStringElements then str - else str.substring(0, str.offsetByCodePoints(0, MaxStringElements - 1)) + showTruncated + else str.substring(0, str.offsetByCodePoints(0, MaxStringElements - 1)) + infoOutputGotTruncated /** Return a String representation of a value we got from `classLoader()`. */ private[repl] def replStringOf(value: Object)(using Context): String = From 2db4447d7cff1f711b82b4fc90a151cd6af23767 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 29 Sep 2022 09:36:46 +0200 Subject: [PATCH 13/14] indicate that truncation happened --- compiler/src/dotty/tools/repl/Rendering.scala | 11 ++++++++++- .../repl/settings-repl-max-print-elements | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/repl/Rendering.scala b/compiler/src/dotty/tools/repl/Rendering.scala index 0f69b3d470d9..6cb89a2b4981 100644 --- a/compiler/src/dotty/tools/repl/Rendering.scala +++ b/compiler/src/dotty/tools/repl/Rendering.scala @@ -75,7 +75,16 @@ private[repl] class Rendering(parentClassLoader: Option[ClassLoader] = None): } } - stringOfMaybeTruncated + (value: Object, maxElements: Int) => { + // `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. + val maybeTruncated = stringOfMaybeTruncated(value, maxElements) + val notTruncated = stringOfMaybeTruncated(value, Int.MaxValue) + if (maybeTruncated.length == notTruncated.length) maybeTruncated + else maybeTruncated + infoOutputGotTruncated + } + } myClassLoader } diff --git a/compiler/test-resources/repl/settings-repl-max-print-elements b/compiler/test-resources/repl/settings-repl-max-print-elements index 710540169595..af2ad511f8bf 100644 --- a/compiler/test-resources/repl/settings-repl-max-print-elements +++ b/compiler/test-resources/repl/settings-repl-max-print-elements @@ -4,4 +4,4 @@ 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) +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 From 253143b04d58046e66a7bef88e79bd950b063af4 Mon Sep 17 00:00:00 2001 From: Michael Pollmeier Date: Thu, 29 Sep 2022 09:39:09 +0200 Subject: [PATCH 14/14] test only that new setting, not the string truncation --- compiler/test-resources/repl/settings-repl-max-print-elements | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test-resources/repl/settings-repl-max-print-elements b/compiler/test-resources/repl/settings-repl-max-print-elements index af2ad511f8bf..b203e689f020 100644 --- a/compiler/test-resources/repl/settings-repl-max-print-elements +++ b/compiler/test-resources/repl/settings-repl-max-print-elements @@ -1,5 +1,5 @@ -scala> 1.to(300).toList -val res0: 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, 22 ... large output truncated, print value to show all +scala> 1.to(200).toList +val res0: 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) scala>:settings -Vrepl-max-print-elements:20