1

How can I stop a REPL-like console application by pressing Ctrl-d, without waiting for user to type Ctr-d then enter?

Here is a code sample :

def isExit(s: String): Boolean = s.head.toInt == 4 || s == "exit"

def main(args: Array[String]) = {
    val continue: Boolean = true
    while(continue){
        println "> "
        io.StdIn.readLine match {
            case x if isExit(x) => println "> Bye!" ; continue = false
            case x              => evaluate(x) 
        }
    }
}

The s.head.toInt == 4is to test if the first char of the input line is a ctrl d.

EDIT : Complete source code to run it :

object Test {

    def isExit(s: String): Boolean = s.headOption.map(_.toInt) == Some(4) || s == "exit"

    def evaluate(s: String) = println(s"Evaluation : $s")

    def main(args: Array[String]) = {
        var continue = true
        while(continue){
            print("> ")
            io.StdIn.readLine match {
                case x if isExit(x) => println("> Bye!") ; continue = false
                case x              => evaluate(x)
            }
        }
    }
}

With this, I got a NullPointerException on the s.headOption (because of a null s)

whisust
  • 176
  • 3
  • 16

2 Answers2

0

minor modifications to the code

def main(args: Array[String]) = {
    var continue: Boolean = true // converted val to var
    while(continue){
        println("> ")
        io.StdIn.readLine match {
            case x if isExit(x) => println("> Bye!") ; continue = false
            case x              => evaluate(x) 
        }
    }
}

your isExit method is not handling a condition where your read line could be empty. so the modified isExit would look like the below. Otherwise your example works as expected

def isExit(s: String): Boolean = s.headOption.map(_.toInt) == Some(4) || s == "exit"
rogue-one
  • 11,259
  • 7
  • 53
  • 75
  • The value of `s` is still `null` on `isExit` on a ctrl-d, so there is still an NullPointerException. – whisust Feb 22 '17 at 15:00
0

Okay, as said in Read Input until control+d the Ctrl-D keypress flushes the line into the JVM. If there is something written on the line, it will be sent (only after two consecutive ctrl-d, I don't know why), otherwise the io.StdIn.readLine will receive an end of stream character and will return null, as indicated in the scala doc http://www.scala-lang.org/api/2.12.0/scala/io/StdIn$.html#readLine():String

Knowing this, we can replace the s.headOption... by a simple s == null to match our needs. Full working example :

object Test {

    def isExit(s: String): Boolean = s == null || s == "exit"

    def evaluate(s: String) = println(s"Evaluation : $s")

    def main(args: Array[String]) = {
        var continue = true
        while(continue){
            print("> ")
            io.StdIn.readLine match {
                case x if isExit(x) => println("Bye!") ; continue = false
                case x              => evaluate(x)
            }
        }
    }
}
Community
  • 1
  • 1
whisust
  • 176
  • 3
  • 16