Skip to content

History

Guillaume Nodet edited this page Oct 24, 2023 · 2 revisions

History

JLine history lets users reuse commands they've entered before. By default, history entries exist only in memory; a subsequent run of the same program will start with a new, empty history.

To preserve a user's history across runs, you need to connect a few bits.

Setting up a user history file.

The DefaultHistory class looks for a path to the user history file in the HISTORY_FILE variable.

You can set the variable when building your reader. If you have a

myLineReaderBuilder.variable(
    LineReader.HISTORY_FILE,
    path
);

You can represent path as a java.io.File, as a java.nio.Path, or as a string.

Different operating systems put user's app-specific files in different places. harawata/appdirs makes it easy to find a user configuration directory specific to your program.

Other history related options

While you're doing that, you can configure other history options.

  • You can also limit the number of history entries stored in the file
    myLineReaderBuilder.variable(
        LineReader.HISTORY_FILE_SIZE,
        1_000 // history entries
    );
  • LineReader.HISTORY_SIZE limits the number of history entries kept in memory.
  • LineReader.HISTORY_IGNORE is a colon-separated list of patterns that will not be saved in history.

On startup

The line reader will load history on the first use, but you can pre-load history if you like:

History history = myLineReader.getHistory();
// Make sure the instance has access to the reader's variable set,
// and load history.
history.attach(myLineReader);

On exit

Your line reader will load history on first use, but it will not save it for you.

On exit, you should be sure to do

myLineReader.getHistory().save(); // May throw IOException

This could happen if the reader loop exits with an Exception. You might put your save call in a finally block, in a shutdown hook, or signal handler.

Race conditions

Beware that if a single user has multiple instances of your program running concurrently, saving history from one could clobber content from another.

This is not usually a problem in practice since the default History implementation takes care to only append history entries from the current session:

              StandardOpenOption.WRITE, StandardOpenOption.APPEND, StandardOpenOption.CREATE)) {
                for (Entry entry : items.subList(from, items.size())) {
                    if (isPersistable(entry)) {
                        writer.append(format(entry));
                    }
                }