Down is an unintrusive user experience upgrade for the ocaml
toplevel (REPL).
To use Down in ocaml
simply issue this phrase:
# #use "down.top" (* or "down.nattop" in ocamlnat *)
You can add this line to your ~/.ocamlinit
file.
The impact of using Down
on the toplevel environment should be minimal: it loads the library down.cma
, makes the Down API accessible by including its library directory, sets standard input in raw mode whenever it asks for user input and installs a signal handler for SIGWINCH
.
This is it.
Down
provides classical readline capability. There is not much to be said about it and should be mostly natural if you are used to command line interfaces. A summary of key bindings is available by invoking:
# Down.help () (* show key bindings and basic help *)
Down
listens for keyboard input until you hit return and the input phrase ends with a trailing ;;
character sequence. At that point it gives it to ocaml
for execution and gets back to you once it has an answer.
Unicode text is supported in a limited manner using a weak form of grapheme clusters based on the data of Uucp.Break.tty_width_hint
. This should be good enough for basic REPL interaction. However it may fail (not too catastrophically) on some of UTF-8 encoded string literals – for example if they contain emoji skin tone modifiers.
The key bindings cannot be customized at the moment (see this issue).
To navigate input history use the up and down arrows of your keyboard.
History is stored accross ocaml
invocations in ~/.config/ocaml/history.ml
. This is a plain text file where history entries are separated by (**)
lines. It may not be a syntactically valid OCaml file since history includes lines that did not parse.
The Down.History
module has a few functions to manipulate history:
# Down.History.edit () (* edit the history in your editor *)
# Down.History.clear () (* clear your history *)
Sessions allow to record, edit, save and replay sequences of phrases. You can see them as named and executable histories or #use
able files available via short and absolute names.
They are useful to quickly setup a given environment or to reliably insert a given sequence of phrases on the prompt if you are making a demonstration with the toplevel (see stepping).
Session management is provided by the Down.Session
module. To list available sessions issue:
# Down.Session.list () (* list the names of available sessions *)
There are different ways of creating sessions. One way of doing so is to call the edit
function
# Down.Session.edit "mysession" (* edit or create a session *)
this opens an OCaml file named mysession.ml
in your editor in which you can insert or modify OCaml toplevel phrases. Once you are done, you can load
the session to execute its phrases:
# Down.Session.load "mysession" (* load and execute a session *)
This is strictly equivalent to issue the #use
toplevel directive on the the session file.
One convention to remember is that the string ""
is used in session functions to refer to the name of the last explicit session name you used with one the session functions. This is persisted accross ocaml
invocation. For example:
# Down.Session.edit "mysession" (* edit session "mysession" *)
# Down.Session.load "" (* load session "mysession" *)
# Down.Session.edit "" (* edit session "mysession" *)
# ^D
> ocaml
...
# Down.Session.load "" (* load session "mysession" *)
Another way of creating a session is to record phrases and eventually save them in a session. To start recording phrases use:
# Down.Session.record () (* start recording input phrases *)
Each phrase subsequently input is added to the recorded phrases. Unfortunately due to toplevel API limitations, phrases that error are also recorded. This is why you may want to revise the recorded phrases from time to time to make them coherent:
# Down.Session.revise () (* revise recorded phrases *)
Once you are done you can save the recorded phrases with save
:
# Down.Session.save "mysetup" (* save recorded phrases *)
This saves the recorded phrases in a new session "mysetup"
, stops recording and clears the recorded phrases. The append
function works like save
but appends the recorded phrases to a session or creates it if it does not exist.
# Down.Session.append "mysetup" (* like save but appends *)
Session "mysetup"
can now be load
ed to play back the recorded phrases. If the sequence happens to have an error when you load it, simply edit
the session to correct it and try again.
If you forget to save the recorded phrases they should be available to revise
, save
or append
the next time you run ocaml
.
Stepping through a session allows to reliably paste a sequence of phrases on your prompt. To step through a session issue:
# Down.Session.steps "mysession" (* define the stepped session *)
You can now use shift-{up,down}
(or C-x C-{n,p}
) to navigate and paste on your prompt the phrases of the session. Like in the history file, steps are delimited by (**)
lines in session files. Here's a session with two steps:
let rec fact = function
| 0 -> 1
| n -> n * fact (n - 1);;
(**)
fact 3;;
A session NAME
is stored in the plain OCaml file ~/.config/ocaml/session/NAME.ml
. Except for the fact that (**)
lines are used to separate steps for stepping there is nothing special about them and you can edit or #use
them directly.
The file ~/.config/ocaml/session/last
holds the name of the last session used.
If you recorded phrases but didn't save them they are persisted in the file ~/.config/ocaml/session/unsaved
and reloaded on the next ocaml
execution.
At the moment completion is provided by ocp-index
and is a bit crude. It is planned to improve it in the future but at the moment it is context and open
unaware, only library identifiers are completed regardless whether those are loaded or not, identifiers defined during toploop interaction are not completable.
To complete an identifier put your cursor at the point of completion and hit TAB:
# List.con^t
List.cons : 'a -> 'a list -> 'a list
List.concat : 'a list list -> 'a list
# List.con
Hitting C-t
with the cursor over an identifier or on the whitespace right after it shows its documentation:
# List.append^C-t
List.append : 'a list -> 'a list -> 'a list
Concatenate two lists. Same as the infix operator [@].
Not tail-recursive (length of the first argument).
# List.append