Search code examples
javaterminalconsoleconsole-input

Java Multi-Line Console Input and Cursor Handling


The following problem arised while building a REPL console application for a programming language. Essentially, the language relies on you being able to write multiple lines of text. However, the ENTER key should also be usable to evaluate an expression.

> 1 + 1<enter>
int int1 = 2
> <cursor>

To allow multi-line input, I am using an algorithm that counts the number of parentheses, braces, brackets and quotes, so if either of them are unbalanced, it inserts a newline:

> if (int1 < 3) {<enter>
|     println "ok"<enter>
|     }<enter>
ok
> <cursor>

The main problem with this approach is that as soon as I press enter on a line, that line cannot be edited anymore. Repeatedly pressing BACKSPACE at the } symbol will first delete it and then stay at that position without removing the newline and allowing me to delete the println line. This makes programs very error-prone and editing very tedious.

Another problem is the fact that I can't even use the cursor in some consoles (like the Mac Terminal). The console will just show ANSI control codes in that event.


Is there a way to solve both problems, perhaps by introducing custom cursor handling to my REPL frontend? How much access does the JVM give me to the enclosing console? Can I force consoles to redirect all key presses to the program? And, do I have to use different techniques for different IDE consoles / terminals / Operating Systems?


Solution

  • The problem is not "the JVM", but the unsophisticated code of JavaRepl. Console input (see the source) is just reading lines from the standard input:

    private static Mapper<Sequence<String>, String> readFromConsole() {
        return new Mapper<Sequence<String>, String>() {
            private final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    
            public String call(Sequence<String> lines) throws Exception {
                return reader.readLine();
            }
        };
    }
    

    BufferedReader really does not do a lot. Its documentation is also not helpful. A description on TutorialsPoint helps;

    The java.io.BufferedReader.readline() method read a line of text. A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), or a carriage return followed immediately by a linefeed.

    To improve JavaRepl as you would like, it would have to be modified to (at least) allow treating the escape sequences sent by left-arrow and keypad-delete the same as "backspace" (actually delete). It is doable (but perhaps not what the developers of JavaRepl intend: compatibility with old versions of Java is likely a consideration). Even the "newer" Console class from Java6 is documented without hinting that it is more than a modest reworking of BufferedReader to help with password prompts.

    For reading escape sequences, see

    To get multi-line editing, another step would be needed to change its interface to allow retrieving prior lines and editing those. I think that is beyond the scope of JavaRepl's design (but you might suggest it to the developers).