Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow JLine to fall back to a dumb terminal #21330

Merged
merged 1 commit into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/repl/JLineTerminal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@ class JLineTerminal extends java.io.Closeable {
// Logger.getLogger("org.jline").setLevel(Level.FINEST)

private val terminal =
TerminalBuilder.builder()
.dumb(dumbTerminal) // fail early if not able to create a terminal
.build()
var builder = TerminalBuilder.builder()
if System.getenv("TERM") == "dumb" then
// Force dumb terminal if `TERM` is `"dumb"`.
// Note: the default value for the `dumb` option is `null`, which allows
// JLine to fall back to a dumb terminal. This is different than `true` or
// `false` and can't be set using the `dumb` setter.
// This option is used at https://github.com/jline/jline3/blob/894b5e72cde28a551079402add4caea7f5527806/terminal/src/main/java/org/jline/terminal/TerminalBuilder.java#L528.
builder.dumb(true)
builder.build()
private val history = new DefaultHistory
def dumbTerminal = Option(System.getenv("TERM")) == Some("dumb")

private def blue(str: String)(using Context) =
if (ctx.settings.color.value != "never") Console.BLUE + str + Console.RESET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ class CoursierScalaTests:
version()

def emptyArgsEqualsRepl() =
val output = CoursierScalaTests.csScalaCmd()
assertTrue(output.mkString("\n").contains("Unable to create a terminal")) // Scala attempted to create REPL so we can assume it is working
val output = CoursierScalaTests.csScalaCmdWithStdin(Seq.empty, Some("println(\"Hello World\")\n:quit"))
assertTrue(output.mkString("\n").contains("Hello World"))
emptyArgsEqualsRepl()

def run() =
Expand Down Expand Up @@ -132,8 +132,8 @@ class CoursierScalaTests:
compileFilesToJarAndRun()

def replWithArgs() =
val output = CoursierScalaTests.csScalaCmd("-source", "3.0-migration")
assertTrue(output.mkString("\n").contains("Unable to create a terminal")) // Scala attempted to create REPL so we can assume it is working
val output = CoursierScalaTests.csScalaCmdWithStdin(Seq("-source", "3.0-migration"), Some("println(\"Hello World\")\n:quit"))
assertTrue(output.mkString("\n").contains("Hello World"))
replWithArgs()

def argumentFile() =
Expand All @@ -148,25 +148,31 @@ class CoursierScalaTests:

object CoursierScalaTests:

def execCmd(command: String, options: String*): (Int, List[String]) =
private def execCmd(command: String, options: Seq[String] = Seq.empty, stdin: Option[String] = None): (Int, List[String]) =
val cmd = (command :: options.toList).toSeq.mkString(" ")
val out = new ListBuffer[String]
val code = cmd.!(ProcessLogger(out += _, out += _))
val process = stdin match
case Some(input) => Process(cmd) #< new java.io.ByteArrayInputStream(input.getBytes)
case None => Process(cmd)
val code = process.!(ProcessLogger(out += _, out += _))
(code, out.toList)

def csScalaCmd(options: String*): List[String] =
csCmd("dotty.tools.MainGenericRunner", options*)
csScalaCmdWithStdin(options, None)

def csScalaCmdWithStdin(options: Seq[String], stdin: Option[String]): List[String] =
csCmd("dotty.tools.MainGenericRunner", options, stdin)

def csScalaCompilerCmd(options: String*): List[String] =
csCmd("dotty.tools.dotc.Main", options*)
csCmd("dotty.tools.dotc.Main", options)

private def csCmd(entry: String, options: String*): List[String] =
private def csCmd(entry: String, options: Seq[String], stdin: Option[String] = None): List[String] =
val (jOpts, args) = options.partition(_.startsWith("-J"))
val newOptions = args match
case Nil => args
case _ => "--" +: args
val newJOpts = jOpts.map(s => s"--java-opt ${s.stripPrefix("-J")}").mkString(" ")
execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" $newJOpts --main-class "$entry" --property "scala.usejavacp=true" --property "scala.use_legacy_launcher=true"""" +: newOptions)*)._2
execCmd("./cs", (s"""launch "org.scala-lang:scala3-compiler_3:${sys.env("DOTTY_BOOTSTRAPPED_VERSION")}" $newJOpts --main-class "$entry" --property "scala.usejavacp=true" --property "scala.use_legacy_launcher=true"""" +: newOptions), stdin)._2

/** Get coursier script */
@BeforeClass def setup(): Unit =
Expand All @@ -177,7 +183,7 @@ object CoursierScalaTests:
case other => fail(s"Unsupported OS for coursier launcher: $other")

def runAndCheckCmd(cmd: String, options: String*): Unit =
val (code, out) = execCmd(cmd, options*)
val (code, out) = execCmd(cmd, options)
if code != 0 then
fail(s"Failed to run $cmd ${options.mkString(" ")}, exit code: $code, output: ${out.mkString("\n")}")

Expand Down