Completion

Rule based

The autocompletion system works on rules, which are pairs of predicates and actions. Predicates help determine when an action is allowed to run, and actions return the actual word list for the completion system to display to the user.

There are a variety of commonly used builtin predicates to make write completions a bit easier, these include cmdname_pred which checks that the current completion is for a specific command name and flag_pred which checks if we are attempting to complete a flag.

Similarly, there are also builtin actions like cmdname_action which returns a list of all executables in the PATH, and filename_action which outputs all the files in the current working directory.

Let's write completion rules for ls

As an example, let's write completion rules for the popular ls command.

Let's begin by initializing the DefaultCompleter. This comes with a couple of sensible rules that most completion engines are expected to have, such as autocompleting the command name from PATH:

use shrs::line::completion::*;

let mut completer = DefaultCompleter::default();

Next, we need to create a rule that will provide completion options for each of the flags ls has. We can do this by writign a rule that first checks if the user has already typed the command ls using cmdname_pred:

let ls_pred = Pred::new(cmdname_pred("ls"));

However, we also want to complete the flags for ls, so we need to also check if we are currently typing a flag. We can use the provided flag_pred for this. Notice how we can chain predicates with .and():

let ls_pred = Pred::new(cmdname_pred("ls")).and(flag_pred);

Next we need to write the action that returns all the possible flags. An action is just a function that takes in CompletionCtx and returns a list of possible completions. Completion holds a bit more metadata that we will not touch for now, but if we just wish to return a list of strings to the completer, we can use the helper function default_format to generate default options for Completion.

let ls_action = Box::new(|ctx: &CompletionCtx| -> Vec<Completion> {
    default_format(vec!["-a".into(), "-l".into(), "-h".into()])
});

And with that we can register our first completion rule:

completer.register(Rule::new(ls_pred, ls_action));

In the end, our resulting code looks like:

use shrs::line::completion::*;

let mut completer = DefaultCompleter::default();

let ls_pred = Pred::new(cmdname_pred("ls")).and(flag_pred);
let ls_action = Box::new(|ctx: &CompletionCtx| -> Vec<Completion> {
    default_format(vec!["-a".into(), "-l".into(), "-h".into()])
});

completer.register(Rule::new(ls_pred, ls_action));

Coming soon: declarative

The plugin shrs_derive_completion provides a declarative way to create completions in the form of a procedual macro. If you are familiar with the crate clap this should feel very familiar.