Skip to content
Ghost Text Suggestions

Ghost Text Suggestions

Ghost text suggestions display inline, dimmed text ahead of the cursor as you type, showing what Aesh predicts you want to enter. Unlike tab completion, which requires pressing Tab, ghost text appears automatically and can be accepted with keyboard shortcuts.

How It Works

As you type, ghost text suggestions appear automatically:

myapp> ca|che                    ← "che" appears dimmed after cursor
myapp> connect --ho|st=          ← "st=" appears dimmed after cursor
myapp> mvn clean tes|t -pl aesh  ← "t -pl aesh" from history, dimmed
  • Right arrow / End – Accept the full suggestion
  • Alt+F / Ctrl+Right – Accept the next word from the suggestion
  • Keep typing – Narrow or dismiss the suggestion
  • Ghost text updates automatically after every keystroke, including backspace

History Suggestions

Aesh Readline includes a built-in HistorySuggestionProvider that suggests commands from your history as you type, similar to fish shell’s auto-suggestions. It searches history from most recent to oldest, finding the first entry that starts with what you’ve typed, and shows the remaining text as ghost text.

myapp> mvn c|lean test -pl aesh -Dtest=ProcessorTest
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ from history

Automatic activation

When you call setSuggestionProvider() on ReadlineConsole, history suggestions are automatically enabled and take priority over command-level suggestions. No additional setup is needed – if the current input matches a history entry, that suggestion is shown; otherwise, the command suggestion provider is used as a fallback.

Standalone history suggestions

If you want history suggestions without a command-level provider (e.g., in a plain readline application):

Readline readline = new Readline(editMode, history, completionHandler);
readline.enableHistorySuggestions();

How it works

  • Searches History.getAll() in reverse order (most recent first)
  • Uses prefix matching – the buffer must match the beginning of a history entry
  • Returns only the suffix (the part you haven’t typed yet)
  • Combined with other providers via CompositeSuggestionProvider (history first)

CommandSuggestionProvider

CommandSuggestionProvider uses the command registry to suggest command names, subcommand names, and option names as you type. It implements the SuggestionProvider interface from Aesh Readline.

What It Suggests

ContextExample InputSuggestion
Command namescache (from cache)
Subcommand namesgit commit (from commit)
Option namesconnect --host= (from --host)

Suggestions are only shown when there is a single unambiguous match. If the input c could match both cache and connect, no suggestion is shown.

For options that accept a value, the suggestion appends = after the option name. Boolean options (flags) do not get the trailing =.

Setup

Create a CommandSuggestionProvider from your command registry and attach it to the console:

import org.aesh.command.impl.completer.CommandSuggestionProvider;
import org.aesh.console.ReadlineConsole;

ReadlineConsole console = new ReadlineConsole(settings);

// Create suggestion provider from the console's registry
CommandSuggestionProvider<?> provider =
        new CommandSuggestionProvider<>(console.getCommandRegistry());

console.setSuggestionProvider(provider);
console.start();

With AeshConsoleRunner

If you use AeshConsoleRunner, you can access the underlying ReadlineConsole to set up suggestions:

ReadlineConsole console = new ReadlineConsole(settings);
console.setPrompt(new Prompt("myapp> "));
console.setSuggestionProvider(
        new CommandSuggestionProvider<>(console.getCommandRegistry()));
console.start();

CompositeSuggestionProvider

Multiple suggestion sources are combined using CompositeSuggestionProvider. The first provider that returns a non-null suggestion wins.

When you call setSuggestionProvider() with history available, Aesh automatically creates a CompositeSuggestionProvider with history suggestions first and your provider second. You typically don’t need to create one manually.

For custom chaining:

import org.aesh.readline.suggestion.CompositeSuggestionProvider;
import org.aesh.readline.suggestion.SuggestionProvider;

SuggestionProvider myProvider = buffer -> {
    // Custom suggestion logic
    return null;
};

CommandSuggestionProvider<?> commandProvider =
        new CommandSuggestionProvider<>(console.getCommandRegistry());

// Custom ordering: your provider first, then commands
CompositeSuggestionProvider composite =
        new CompositeSuggestionProvider(myProvider, commandProvider);

console.setSuggestionProvider(composite);

Custom SuggestionProvider

You can implement the SuggestionProvider interface directly for custom suggestion logic:

import org.aesh.readline.suggestion.SuggestionProvider;

SuggestionProvider myProvider = buffer -> {
    // Return the suffix to append as ghost text, or null for no suggestion
    if (buffer.startsWith("hel")) {
        return "lo";
    }
    return null;
};

console.setSuggestionProvider(myProvider);

The suggest method receives the current input buffer and returns the text to display after the cursor as ghost text. Return null when there is no suggestion.

TailTipSuggestionProvider

TailTipSuggestionProvider displays parameter hints after the cursor, showing remaining options and arguments that haven’t been provided yet. Unlike CommandSuggestionProvider which completes the current word, tail tips show the full synopsis of what comes next.

myapp> deploy --force |[-e=<environment>] <app>
                       ^^^^^^^^^^^^^^^^^^^^^^^^^ remaining params (dimmed)

As the user fills in parameters, the tail tip updates to show only what’s still needed:

myapp> deploy --force -e prod |<app>
                               ^^^^^ only argument left (dimmed)

How it works

  • Only activates when the buffer ends with a space (the user has finished typing a word)
  • Parses the current buffer to determine which options have already been provided
  • Builds a synopsis of remaining options and arguments
  • Uses the same formatting as --help synopsis (short flag clusters, exclusive pipes, value placeholders)
  • Caches the last result to avoid re-parsing on unchanged input

Setup

Tail tips are opt-in via Settings and only apply to interactive ReadlineConsole mode (not AeshRuntimeRunner):

Settings settings = SettingsBuilder.builder()
        .tailTipSuggestions(true)   // default: false
        .commandRegistry(registry)
        .build();

ReadlineConsole console = new ReadlineConsole(settings);
console.start();

When enabled, the tail tip provider is automatically added as the lowest-priority provider in the suggestion chain. History suggestions and command auto-suggest take priority when they have a match.

Priority ordering

When tailTipSuggestions is enabled and setSuggestionProvider() is also used, the chain is:

  1. History suggestions (if history is available)
  2. Your suggestion provider (e.g., CommandSuggestionProvider)
  3. Tail tip suggestions (only when others return null)

This means mid-word typing gets auto-suggest, and after completing a word (trailing space), the tail tip shows remaining parameters.

Combining with CommandSuggestionProvider

For the best experience, use both providers together:

Settings settings = SettingsBuilder.builder()
        .tailTipSuggestions(true)
        .commandRegistry(registry)
        .build();

ReadlineConsole console = new ReadlineConsole(settings);
console.setSuggestionProvider(
        new CommandSuggestionProvider<>(console.getCommandRegistry()));
console.start();

This gives:

  • History auto-suggest when a history entry matches
  • Command/option name completion while typing
  • Parameter hints after each completed word

Ghost Text vs Tab Completion

FeatureGhost TextTab Completion
TriggerAutomatic as you typePress Tab
DisplayInline dimmed textList below prompt
MatchesSingle unambiguous onlyShows all matches
AcceptRight arrowTab / Enter
ScopeCommands, subcommands, options, tail tipsOptions, arguments, custom values

Both features complement each other. Ghost text gives fast inline suggestions for unambiguous matches, while tab completion helps explore all available options when there are multiple possibilities.