diff --git a/compiler/src/dotty/tools/repl/ParseResult.scala b/compiler/src/dotty/tools/repl/ParseResult.scala index 24a624173050..6c9f95d4dca2 100644 --- a/compiler/src/dotty/tools/repl/ParseResult.scala +++ b/compiler/src/dotty/tools/repl/ParseResult.scala @@ -93,6 +93,10 @@ object Reset { val command: String = ":reset" } +/** Toggle automatic printing of results */ +case object Silent extends Command: + val command: String = ":silent" + /** `:quit` exits the repl */ case object Quit extends Command { val command: String = ":quit" @@ -113,6 +117,7 @@ case object Help extends Command { |:imports show import history |:reset [options] reset the repl to its initial state, forgetting all session entries |:settings update compiler options, if possible + |:silent disable/enable automatic printing of results """.stripMargin } @@ -137,6 +142,7 @@ object ParseResult { TypeOf.command -> (arg => TypeOf(arg)), DocOf.command -> (arg => DocOf(arg)), Settings.command -> (arg => Settings(arg)), + Silent.command -> (_ => Silent), ) def apply(source: SourceFile)(using state: State): ParseResult = { diff --git a/compiler/src/dotty/tools/repl/ReplDriver.scala b/compiler/src/dotty/tools/repl/ReplDriver.scala index 486005658d79..3a2b8ce00bbe 100644 --- a/compiler/src/dotty/tools/repl/ReplDriver.scala +++ b/compiler/src/dotty/tools/repl/ReplDriver.scala @@ -60,12 +60,14 @@ import scala.util.Using * @param valIndex the index of next value binding for free expressions * @param imports a map from object index to the list of user defined imports * @param invalidObjectIndexes the set of object indexes that failed to initialize + * @param quiet whether we print evaluation results * @param context the latest compiler context */ case class State(objectIndex: Int, valIndex: Int, imports: Map[Int, List[tpd.Import]], invalidObjectIndexes: Set[Int], + quiet: Boolean, context: Context): def validObjectIndexes = (1 to objectIndex).filterNot(invalidObjectIndexes.contains(_)) @@ -114,7 +116,7 @@ class ReplDriver(settings: Array[String], } /** the initial, empty state of the REPL session */ - final def initialState: State = State(0, 0, Map.empty, Set.empty, rootCtx) + final def initialState: State = State(0, 0, Map.empty, Set.empty, false, rootCtx) /** Reset state of repl to the initial state * @@ -217,11 +219,6 @@ class ReplDriver(settings: Array[String], interpret(ParseResult.complete(input)) } - final def runQuietly(input: String)(using State): State = runBody { - val parsed = ParseResult(input) - interpret(parsed, quiet = true) - } - protected def runBody(body: => State): State = rendering.classLoader()(using rootCtx).asContext(withRedirectedOutput(body)) // TODO: i5069 @@ -290,10 +287,10 @@ class ReplDriver(settings: Array[String], .getOrElse(Nil) end completions - protected def interpret(res: ParseResult, quiet: Boolean = false)(using state: State): State = { + protected def interpret(res: ParseResult)(using state: State): State = { res match { case parsed: Parsed if parsed.trees.nonEmpty => - compile(parsed, state, quiet) + compile(parsed, state) case SyntaxErrors(_, errs, _) => displayErrors(errs) @@ -311,7 +308,7 @@ class ReplDriver(settings: Array[String], } /** Compile `parsed` trees and evolve `state` in accordance */ - private def compile(parsed: Parsed, istate: State, quiet: Boolean = false): State = { + private def compile(parsed: Parsed, istate: State): State = { def extractNewestWrapper(tree: untpd.Tree): Name = tree match { case PackageDef(_, (obj: untpd.ModuleDef) :: Nil) => obj.name.moduleClassName case _ => nme.NO_NAME @@ -362,11 +359,9 @@ class ReplDriver(settings: Array[String], given Ordering[Diagnostic] = Ordering[(Int, Int, Int)].on(d => (d.pos.line, -d.level, d.pos.column)) - if (!quiet) { - (definitions ++ warnings) - .sorted - .foreach(printDiagnostic) - } + (if istate.quiet then warnings else definitions ++ warnings) + .sorted + .foreach(printDiagnostic) updatedState } @@ -542,6 +537,8 @@ class ReplDriver(settings: Array[String], rootCtx = setupRootCtx(tokenize(arg).toArray, rootCtx) state.copy(context = rootCtx) + case Silent => state.copy(quiet = !state.quiet) + case Quit => // end of the world! state diff --git a/compiler/test-resources/repl/silent b/compiler/test-resources/repl/silent new file mode 100644 index 000000000000..9e851e8adb01 --- /dev/null +++ b/compiler/test-resources/repl/silent @@ -0,0 +1,25 @@ +scala>:silent +scala> 1+1 +scala> case class A(x: Int) +scala> A("string") +-- [E007] Type Mismatch Error: ------------------------------------------------- +1 | A("string") + | ^^^^^^^^ + | Found: ("string" : String) + | Required: Int + | + | longer explanation available when compiling with `-explain` +1 error found +scala> Option[Int](2) match { case Some(x) => x } +1 warning found +-- [E029] Pattern Match Exhaustivity Warning: ---------------------------------- +1 | Option[Int](2) match { case Some(x) => x } + | ^^^^^^^^^^^^^^ + | match may not be exhaustive. + | + | It would fail on pattern case: None + | + | longer explanation available when compiling with `-explain` +scala>:silent +scala> 1 + 2 +val res2: Int = 3 diff --git a/compiler/test/dotty/tools/repl/TabcompleteTests.scala b/compiler/test/dotty/tools/repl/TabcompleteTests.scala index 95419824d9d1..01b9d1109994 100644 --- a/compiler/test/dotty/tools/repl/TabcompleteTests.scala +++ b/compiler/test/dotty/tools/repl/TabcompleteTests.scala @@ -217,6 +217,7 @@ class TabcompleteTests extends ReplTest { ":quit", ":reset", ":settings", + ":silent", ":type" ), tabComplete(":")