UTop: a much improved interface to the OCaml toplevel
This is a post about the utop
toplevel provided in the OPAM
repository as an alternative to the standard OCaml one.
OCaml comes with an interactive toplevel. If you type ocaml
in a
shell you will get a prompt where you can type OCaml code that is
compiled and executed on the fly.
$ ocaml
OCaml version 4.02.0+dev12-2014-07-30
# 1 + 1;;
- : int = 2
You can load libraries and your own modules in the toplevel, and it is great for playing with your code. You'll quickly notice that the user experience is not ideal, as there is no editing support: you cannot conveniently change what you type nor can you rewind to previously typed phrases.
This can be improved by using tools such as
ledit or
rlwrap which adds line editing
support for any program: rlwrap ocaml
. This is better but still
doesn't provide fancy features such as context sensitive completion.
That's why UTop was started. UTop is a shiny frontend to the OCaml interactive toplevel, which tries to focus on the user experience and features:
- interactive line editing
- real-time tab completion of functions and values
- syntax highlighting
And many other things which make life easier for users that have been added over time.
What does UTop stand for?
UTop stands for Universal Toplevel
. Universal because it can be used
in a terminal or in Emacs (I originally planned to add a windowed
version using GTK but unfortunately never completed it).
The UTop prompt
The utop prompt looks much more 'blinky' than the one of the default toplevel. Install it using OPAM very simply:
opam install utop
eval `opam config env` # may not be needed
utop
This is typically what you see when you start utop:
─( 16:36:52 )─< command 0 >───────────────────────{ counter: 0 }─
utop #
┌───┬────────────┬─────┬───────────┬──────────────┬───────┬─────┐
│Arg│Arith_status│Array│ArrayLabels│Assert_failure│Big_int│Bigar│
└───┴────────────┴─────┴───────────┴──────────────┴───────┴─────┘
It displays:
- the time
- the command number
- the macro counter (for Emacs style macros)
The box at the bottom is for completion, which is described in the next section.
If the colors seem too bright you can type #utop_prompt_fancy_light
,
which is better for light backgrounds. This can be set permanently by
adding the line to ~/.ocamlinit
or by adding profile: light
to
~/.utoprc
.
The prompt can be customized by the user, by setting the reference
UTop.prompt
:
utop # UTop.prompt;;
- : LTerm_text.t React.signal ref = {contents = <abstr>}
LTerm_text.t
is for styled text while React.signal
means that it
is a reactive signal, from the
react library. This makes it very
easy to create a prompt where the time is updated every second for
instance.
Real-time completion
This is the main feature that motivated the creation of UTop. UTop makes use of the compiler internals to find possible completions on:
- function names
- function argument names
- constructor names
- record fields
- method names
Instead of the classic way of displaying a list of words when the user press TAB, I chose to dynamically display the different possibilities as the user types. This idea comes from the dmenu tool from the dwm window manager.
The possible completions are displayed in the completion bar below the
prompt. It is possible to navigate in the list by using the meta key
(Alt
by default most of the time) and the left and right arrows. A
word can be selected by pressing the meta key and TAB
. Also pressing
just TAB
will insert the longest common prefix of all possibilities.
Syntax highlighting
UTop can do basic syntax highlighting. This is disabled by default but
can be enabled by writing a ~/.utoprc
file. You can copy one from
the repository, either for
dark background
or
light background.
Emacs integration
As said earlier UTop can be run in Emacs. Instructions to set this up can be found in UTop's readme. The default toplevel can also be run this way but UTop is better in the following respects:
- it provides context-sensitive completion
- it behaves like a real shell, i.e. you cannot delete the prompt
They are several Emacs libraries for writing shell-like modes but I wrote my own because with all of the ones I found it is possible to insert or remove characters from the prompt, which I found frustrating Even with the mode used by the Emacs Shell mode it is possible. AFAIK at the time I wrote it the UTop mode was the only one where it was really impossible to edit the something in the frozen part of the buffer.
Other features
This is a non-exhaustive list of features that have been added over time to enhance the user experience. Some of them might be controversial, so I tried to choose what was the most requested most of the time.
- when using the lwt or async libraries, UTop will automatically wait for ['a Lwt.t] or ['a Deferred.t] values and return the ['a] instead
- made
-short-paths
the default. This option allow to display shorter types when using packed libraries such as core - hide identifiers starting with
_
to the user. This is for hiding the churn generated by syntax extensions. This can be disabled withUTop.set_hide_reserved
or with the command line argument-show-reserved
. - automatically load
camlp4
when the user requests a syntax extension. In the default toplevel one has to type#camlp4
first. - hide verbose messages from the
findlib
library manager. - add a
typeof
directive to show the type of modules and values. - automatically load files from
$OCAML_TOPLEVEL_PATH/autoload
at startup. - allow to specify libraries to be loaded on the command line.
Libraries developed to support UTop
For the needs of UTop I wrote lambda-term, which is kind of an equivalent of ncurses+readline, but written in OCaml. It was written because I wasn't happy with the ncurses API and I wanted something more fancy than readline, especially for completion. In the end I believe that it is much more fun to write terminal applications in OCaml using lambda-term.
The pure editing part is managed by the zed library, which is independent from the user interface.
UTop development
Utop is fairly feature-complete, and so I don't spend much time on it these days. It became the recommended toplevel to use with the Real World OCaml book, and most users are happier with the interactive interface than using the traditional toplevel.
Many thanks to Peter Zotov who recently joined the project to keep it up-to-date and add new features such as extension point support. Contributions from others (particularly around editor integration) are very welcome, so if you are interested on hacking on it get in touch via the GitHub issue tracker or via the OCaml Platform mailing list.