Learn Yourself a Better REPL for Great Good!

New York, NY · 2013-01-20

Categories: rlwrap, REPL, shell, sml, lua, scheme, yak-shaving

Problem

Recently I’ve been working with REPLs a lot. In many cases, the REPL has Readline support built in. This means that you can use the Up and Down arrows to scroll through the history of the commands you’ve entered, and often you will even be able to auto-complete language builtins using Tab. If you are already comfortable with Readline from your shell, then you will be able to use Ctrl-a and the like immediately in your REPL of choice. In all these ways and more, Readline makes you far more productive.

Some REPLs, however, have no support for Readline. That means that if you enter an Up arrow, you get garbage output like this:

$ sml
Standard ML of New Jersey v110.75 [built: Fri Jan 11 10:32:43 2013]
- ^[[A

Solution

Luckily, there’s a handy and simple solution for cases like this: rlwrap. Simply install rlwrap via your operating system’s package manager (or manually), and then invoke the REPL via rlwrap:

$ rlwrap sml

(Edit: On OSX, you definitely want to use a package manager to install rlwrap – for example, Homebrew or MacPorts – since you’re likely to have trouble installing it otherwise. The problem is that newer versions of OSX don’t use GNU Readline. Thanks to a Hacker News commenter for pointing this out to me.)

At this point, even if you do nothing else, the REPL is now at least able to handle Up and Down arrows to scroll through history, and you can use basic Readline commands like Ctrl-a and Ctrl-e to go to the start and end of the line respectively, Ctrl-u to delete back to the prompt (and save the deleted material), Ctrl-y to paste saved text back into the prompt and so on. One other nice thing is that rlwrap saves history per command across invocations. That means that the next time you use that same REPL, your previous commands are still in rlwrap’s history. Lovely.

But Wait: There’s More

What I’ve covered is already great, but rlwrap can do even better. You can customize its behavior in various ways. I’m going to run through one customization that’s fast and simple, but very powerful. When you invoke rlwrap, you can feed it a file filled with additional completions. rlwrap reads the file before starting, splits the file into words and then supports Tab-completion for all those additional words. For example, if you create a file with all of SML’s keywords, then you can load those into rlwrap when you start SML’s REPL:

rlwrap -f keywords sml

Now your SML REPL will autocomplete those keywords using the Tab key.

Sure,” you say, “but that’s a little annoying.” You think it’s a pain to enter -f <filename> every time, and it also means you must keep track of where that file is, and so on and so forth.

No problem: rlwrap has you covered. Create a directory somewhere and set an environment variable RLWRAP_HOME in your shell to point to that directory. For example:

mkdir ~/.rlwrap
# Added to my .bashrc
export RLWRAP_HOME="$HOME/.rlwrap"

Now simply save your keywords file in that directory as $RLWRAP_HOME/<command>_completions. rlwrap will now automagically load those completions every time you call rlwrap command. (I’ve gone a little fast here. For further details see man rlwrap, particularly the section on files.)

Conclusion

I’m embarrassed to admit that I had not heard of rlwrap until this weekend. But I’m awfully glad I met it. I see hours of happy yak-shaving in my future as I customize it. Most importantly, now my life in the REPLs for SML, Lua and Chicken Scheme, all of which are very minimal, will be far more productive.