I have students running Windows, Linux, and Mac OS X, and I like to encourage students to choose whatever platform makes them most productive. But I also like to be able to give out one set of instructions, grading scripts, etc. to everyone. Fortunately, bash is available everywhere, even on Windows, in the form of Cygwin.
Of course, some students groan because
Cygwin can be a
pain, with the DOS vs. Unix paths, separators, and line endings. But what's
the alternative? To only learn Windows stuff? To learn two sets of commands for
everything? I think it is much better to learn one shell language and get good
at it.
That's just by way of background, to explain my interest in Cygwin. Today, I want to write down what I found out about running the Scala REPL from Cygwin, so that I have a record for the next time I have to go down the Windows sewer hole.
The Scala REPL uses the JLine library for reading user input. This enables history recall, editing of the current line, and tab completion. Lots of people have trouble with JLine and Cygwin and report various workarounds, and not just with Scala. The Jython and Clojure REPL have similar issues.
1. JLine and Cygwin/rxvt
The key observation is that JLine works (kind of—see below) inside a
cmd
window (the one with the ridiculous mechanism for copy/paste)
and its saner variants such as Console. But it does not
work with rxvt. rxvt is popular with
Cygwin users because copy/paste is easy, it is a part of Cygwin, and it is less
flaky (see below).
To make JLine work with rxvt in Cygwin, we need to
stty
calls to adjust terminal settings:
stty -icanon min 1 -echo # Run Scala (without exec) stty icanon echo
-Djline.terminal=jline.UnixTerminal
scala
and return it after the
second stty
Like this:
cygterm=false if $cygwin ; then case "$TERM" in rxvt* | xterm*) cygterm=true ;; esac fi EXEC=exec if $cygterm ; then EXEC= JAVA_OPTS="$JAVA_OPTS -Djline.terminal=jline.UnixTerminal" stty -icanon min 1 -echo > /dev/null 2>&1 fi $EXEC "${JAVACMD:=java}" $JAVA_OPTS -cp "$TOOL_CLASSPATH" \ -Dscala.home="$SCALA_HOME" -Denv.emacs="$EMACS" \ scala.tools.nsc.MainGenericRunner "$@" if $cygterm ; then # Record the exit status immediately, or it will be overridden. SCALA_STATUS=$? stty icanon echo > /dev/null 2>&1 exit $SCALA_STATUS fi
2. JLine and Cygwin/cmd
Using the cmd
or Console window with JLine
is more problematic. With the default JLine options, the arrow and tab keys
work fine, but redirection does not work. For example,
echo 'println("Hello and goodbye")' | scala
hangs, waiting for keyboard input.
If you add the -Djline.terminal=jline.UnixTerminal
option, the
arrow keys and redirection work, but tab completion doesn't.
Adding the stty
commands doesn't help in this case.
3. JLine/Scala and Emacs
As I poked around, I wondered about the -Denv.emacs="$EMACS"
setting. This isn't a JLine option. Instead, the Scala REPL doesn't use JLine
when env.emacs
is set to a non-empty value.
That's unfortunate. In Emacs, there are two ways of running Scala.
In shell mode (M-x shell
or M-x scala-run-scala
),
Emacs takes over the command line editing, and indeed JLine should be turned
off. In that case, TERM
is dumb
and
EMACS
is t
. All works well, except for tab
completion. There is probably no hope to get that to work.
In terminal mode (M-x term
), TERM
is
eterm
and EMACS
is something like 23.1.1
(term:0.96)
. Then JLine should not be turned off because all works as it
should, even tab completion. This is the only way I know for tab completion in
the Scala REPL from inside Emacs. So, a better script would use
case "$EMACS" in t) JAVA_OPTS="$JAVA_OPTS -Denv.emacs=$EMACS" esac
For love or money, I could not get M-x term
to work in Windows.
The Emacs in Cygwin doesn't handle arrow keys, and with the standard Emacs for
Windows, I get an error "Spawning child process: invalid argument".
It works fine in Linux, of course.
Conclusions
cmd
or Console.M-term
.
But don't try that from Windows.