package down

  1. Overview
  2. Docs

Down manual

Down is an unintrusive user experience upgrade for the ocaml toplevel (REPL).

Setup

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.

Line edition

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

History

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

Sessions allow to record, edit, save and replay sequences of phrases. You can see them as named and executable histories or #useable 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).

Basics

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" *)

Recording phrases

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 loaded 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 phrases

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;;

Where is the data ?

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.

Identifier completion and documentation

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