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 sttyLike 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.