Suggestions

Suggestions

Suggestions are a powerful feature in Imperat that allow you to provide helpful hints to users as they type commands.

Using Suggestions

Imperat does not control how suggestions are displayed to the user, as this is determined by the platform's implementation of the Source interface. However, Imperat provides a way for you to define and provide suggestions for command arguments, which can enhance the user experience by offering real-time feedback and guidance on what inputs are expected.

Moreover, Imperat integrates with the platform's suggestion system to provide dynamic suggestions based on the context of the command and the input provided by the user. This allows you to create a more interactive and user-friendly command system, where users can easily discover available options and understand the expected input format for each argument.

Suggestions are classified into static and dynamic suggestions, based on how they are retrieved/generated.

  • Static Suggestions: These are predefined suggestions that are always the same regardless of the context or user input. They are often used for arguments that have a fixed set of valid values, such as a list of available ranks or game modes.
  • Dynamic Suggestions: These are suggestions that are generated based on the current context, such as the user's input or other factors.

Dynamic suggestions can be particularly useful for arguments that require more complex input or have a large number of valid values, as they can provide real-time feedback and guidance to users as they type.

What is SuggestionResolver?

A SuggestionResolver is a functional interface that defines a method for providing suggestions based on the current input and context of a command execution. It's the core component responsible for generating suggestions for command arguments, allowing you to create dynamic and context-aware suggestions.

It can also be used to create static suggestions by returning a fixed CACHED list of suggestions regardless of the input or context.

Setting Suggestions for Arguments

It differs based on the type of suggestions you want to provide, whether they are static or dynamic.

Static Suggestions

To set static suggestions for particular arguments in your pathway, annotate the parameter with @Suggest, as follows:

public class ExampleCommand {

    @RootCommand("example")
    public void example(PLATFORMSOURCE source, @Suggest("option1", "option2", "option3") String option) {
        // Command logic here
    }
}

Dynamic Suggestions

To set dynamic suggestions for particular arguments in your pathway, you should use @SuggestionProvider annotation on the parameter representing the argument, and provide the class of the SuggestionProvider implementation that will generate the suggestions, this involves two main steps:

  1. Create a class that implements the SuggestionProvider interface and implement the provide method to generate suggestions based on the context and input.
  2. Annotate the parameter in your command with @SuggestionProvider and specify the class of your SuggestionProvider implementation.

First, let's create a SuggestionProvider implementation that generates dynamic suggestions based on a registry of ranks:

public class OptionsSuggestionProvider implements SuggestionProvider<PLATFORMSOURCE> {
    @Override
    public List<String> provide(SuggestionContext<PLATFORMSOURCE> context, Argument<PLATFORMSOURCE> parameter) {
        // Example logic to generate suggestions based on input
        return Arrays.asList("option1", "option2", "option3");
    }
}

Then, you can use this SuggestionProvider in your command to provide dynamic suggestions for the option argument:

@RootCommand("example")
public class ExampleCommand {

    @Execute
    public void example(PLATFORMSOURCE source, @SuggestionProvider(OptionsSuggestionProvider.class) String option) { //[!code focus]
        // Command logic here
    }
}

If your implementation(of SuggestionProvider)'s constructor requires parameters/dependencies, you MUST provide a way for imperat to instantiate it. Please refer to the InstanceFactory section for more details on how to achieve this.

Suggestions for custom types

When creating custom argument types, you can also provide suggestions for them by overriding the getSuggestionProvider method in your custom argument type implementation. This allows you to provide context-aware suggestions for your custom types. Here's an example for providing suggestions of available ranks for a custom Rank type:

public class RankArgumentType extends SimpleArgumentType<PLATFORMSOURCE, Rank> {

    @Override
    public @Nullable Rank parse(
            @NotNull CommandContext<PLATFORMSOURCE> context,
            @NotNull Argument<PLATFORMSOURCE> argument,
            @NotNull String input
    ) throws CommandException {
        Rank rank = yourRankRegistry.getRank(input);
        if (rank == null) {
            throw new UnknownRankException(input);
        }
        // for testing purposes, we will just return a new Rank with the name equal to the input
        return rank;
    }

    @Override //assuming #getAllRanks returns a `List<Rank>`
    public SuggestionProvider<PLATFORMSOURCE> getSuggestionProvider() {
        return (ctx, arg)-> {
            return yourRankRegistry.getAllRanks().stream()
                    .map(Rank::getName)
                    .toList();
        };
    }
}

and then use this custom argument type in your command:

@RootCommand("setrank")
public class RankCommand {

    @Execute
    public void exec(PLATFORMSOURCE source, String user, Rank rank) {
        // syntax: /setrank <user> <rank>
    }
}

Thus, when the user starts typing the rank argument or presses TAB for suggestions, they will receive real-time suggestions of valid ranks based on the available ranks in the registry.

Setting argument-specific suggestions using the @SuggestionProvider annotation takes precedence over the suggestions provided by the argument type's getSuggestionProvider method. This means that if you annotate a parameter with @SuggestionProvider, the suggestions generated by that provider will be used instead of the suggestions provided by the argument type, allowing you to customize suggestions on a per-argument basis when needed.