Skip to content

Commit

Permalink
Allow JLine to fall back to a dumb terminal (#21330)
Browse files Browse the repository at this point in the history
Set the `dumb` JLine option to `null` instead of `false` when a dumb
terminal is not forced. This allows JLine to fall back to a dumb
terminal if needed.

Fixes #11978.

Before:

```
➜ ~/dotty git:(mb/11978) ✗ echo 'object Example { def smth = "smth" }' | bin/scala
The `--offline` option is experimental
Please bear in mind that non-ideal user experience should be expected.
If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli
Exception in thread "main" java.lang.IllegalStateException: Unable to create a terminal
        at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:394)
        at org.jline.terminal.TerminalBuilder.build(TerminalBuilder.java:362)
        at dotty.tools.repl.JLineTerminal.<init>(JLineTerminal.scala:34)
        at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:145)
        at dotty.tools.repl.ReplDriver.tryRunning(ReplDriver.scala:136)
        at dotty.tools.repl.Main$.main(Main.scala:7)
        at dotty.tools.repl.Main.main(Main.scala)
        Suppressed: java.io.IOException: Unable to find terminal provider ffm
                at org.jline.terminal.spi.TerminalProvider.load(TerminalProvider.java:74)
                at org.jline.terminal.TerminalBuilder.checkProvider(TerminalBuilder.java:667)
                at org.jline.terminal.TerminalBuilder.getProviders(TerminalBuilder.java:632)
                at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:395)
                ... 6 more
        Suppressed: java.io.IOException: Unable to find terminal provider jni
                at org.jline.terminal.spi.TerminalProvider.load(TerminalProvider.java:74)
                at org.jline.terminal.TerminalBuilder.checkProvider(TerminalBuilder.java:667)
                at org.jline.terminal.TerminalBuilder.getProviders(TerminalBuilder.java:634)
                at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:395)
                ... 6 more
        Suppressed: java.io.IOException: Unable to find terminal provider jansi
                at org.jline.terminal.spi.TerminalProvider.load(TerminalProvider.java:74)
                at org.jline.terminal.TerminalBuilder.checkProvider(TerminalBuilder.java:667)
                at org.jline.terminal.TerminalBuilder.getProviders(TerminalBuilder.java:636)
                at org.jline.terminal.TerminalBuilder.doBuild(TerminalBuilder.java:395)
                ... 6 more
```

After:

```
➜  ~/dotty git:(mb/11978) echo 'object Example { def smth = "smth" }' | bin/scala
The `--offline` option is experimental
Please bear in mind that non-ideal user experience should be expected.
If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli
Aug 05, 2024 3:53:49 PM org.jline.utils.Log logr
WARNING: Unable to create a system terminal, creating a dumb terminal (enable debug logging for more information)
Welcome to Scala 3.6.0-RC1-bin-SNAPSHOT-git-dce4377 (17.0.8, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> // defined object Example

scala> %                                                                                                                         
➜  ~/dotty 
```
  • Loading branch information
smarter authored Aug 6, 2024
2 parents fd45847 + 312d33f commit bad02ab
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 15 deletions.
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

0 comments on commit bad02ab

Please sign in to comment.