Skip to main content
Blade provides two suggestion builder interfaces for implementing tab completion in commands: SuggestionsBuilder for basic string suggestions and RichSuggestionsBuilder for advanced completions with tooltips and metadata.

Overview

Suggestion builders are used in the suggest() and suggestRich() methods of argument providers to provide tab completion suggestions to users.

SuggestionsBuilder

The basic suggestion builder for string-based tab completions.

Class Definition

public final class SuggestionsBuilder
Package: me.vaperion.blade.util.command

Methods

suggest(String suggestion)
void
Adds a suggestion to the list.If the suggestion already exists, it will not be added again. Only suggestions that pass the current filter will be included.
suggestion
String
required
The suggestion text to add
@Override
public void suggest(@NotNull Context ctx,
                   @NotNull InputArgument arg,
                   @NotNull SuggestionsBuilder suggestions) {
    suggestions.suggest("creative");
    suggestions.suggest("survival");
    suggestions.suggest("adventure");
    suggestions.suggest("spectator");
}
setFilter(Predicate<String> filter)
void
Sets a filter for suggestions. Only suggestions that pass the filter will be included in the final list.
filter
Predicate<String>
required
The filter predicate, or null to accept all suggestions
// Filter to only show suggestions starting with "pl"
suggestions.setFilter(s -> s.toLowerCase().startsWith("pl"));
suggestions.suggest("player");   // Included
suggestions.suggest("plot");     // Included
suggestions.suggest("creative"); // Filtered out
testFilter(String suggestion)
boolean
Tests whether a suggestion passes the current filter.
suggestion
String
required
The suggestion to test
Returns: true if the suggestion passes the filter, false otherwise
if (suggestions.testFilter("player")) {
    suggestions.suggest("player");
}
clear()
void
Clears all current suggestions.
suggestions.clear();
count()
int
Returns the number of suggestions currently stored.
int count = suggestions.count();
build()
List<String>
Builds and returns an unmodifiable list of all suggestions.
List<String> finalSuggestions = suggestions.build();

Basic Usage Example

public class GameModeProvider implements ArgumentProvider<GameMode> {
    @Override
    public GameMode provide(@NotNull Context ctx, @NotNull InputArgument arg) {
        String value = arg.requireValue().toLowerCase();
        
        try {
            return GameMode.valueOf(value.toUpperCase());
        } catch (IllegalArgumentException e) {
            throw BladeParseError.recoverable(
                "Invalid game mode: " + value
            );
        }
    }
    
    @Override
    public void suggest(@NotNull Context ctx,
                       @NotNull InputArgument arg,
                       @NotNull SuggestionsBuilder suggestions) {
        // Add all game modes
        for (GameMode mode : GameMode.values()) {
            suggestions.suggest(mode.name().toLowerCase());
        }
    }
}

RichSuggestionsBuilder

An advanced interface for building suggestions with rich metadata like tooltips and numeric completions. This is particularly useful for Brigadier-based platforms.

Interface Definition

public interface RichSuggestionsBuilder
Package: me.vaperion.blade.util.command

Methods

suggest(String text)
void
Adds a string suggestion.
text
String
required
The suggested text
suggestions.suggest("creative");
suggestions.suggest("survival");
suggest(String text, SuggestionTooltip tooltip)
void
Adds a string suggestion with a tooltip.
text
String
required
The suggested text
tooltip
SuggestionTooltip
The tooltip metadata, or null
suggestions.suggest("creative", 
    SuggestionTooltip.text("Creative mode - unlimited resources"));
suggestions.suggest("survival",
    SuggestionTooltip.text("Survival mode - gather and survive"));
suggest(int value)
void
Adds an integer suggestion.
value
int
required
The suggested integer value
// Suggest numbers 1-10
for (int i = 1; i <= 10; i++) {
    suggestions.suggest(i);
}
suggest(int value, SuggestionTooltip tooltip)
void
Adds an integer suggestion with a tooltip.
value
int
required
The suggested integer value
tooltip
SuggestionTooltip
The tooltip metadata, or null
suggestions.suggest(1, SuggestionTooltip.text("One player"));
suggestions.suggest(2, SuggestionTooltip.text("Two players"));
input()
String
Returns the full input string used for suggestion generation.
String fullInput = suggestions.input();
// "/gamemode cre" -> "cre"
start()
int
Returns the start offset where suggestions should replace text.
int startPos = suggestions.start();
remaining()
String
Returns the remaining input segment from the start position.
String remaining = suggestions.remaining();
// Input: "/gamemode cre"
// remaining: "cre"
remainingLowerCase()
String
Returns the lowercase remaining input segment.Useful for case-insensitive filtering.
String lower = suggestions.remainingLowerCase();

for (String name : playerNames) {
    if (name.toLowerCase().startsWith(lower)) {
        suggestions.suggest(name);
    }
}
add(RichSuggestionsBuilder other)
void
Adds all suggestions from another builder instance.
other
RichSuggestionsBuilder
required
The builder to merge from
suggestions.add(otherBuilder);
createOffset(int start)
RichSuggestionsBuilder
Creates a new builder view with the given replacement offset.
start
int
required
The replacement start offset
RichSuggestionsBuilder offset = suggestions.createOffset(5);
restart()
RichSuggestionsBuilder
Creates a new builder view that restarts at this builder’s start offset.
RichSuggestionsBuilder restarted = suggestions.restart();
legacyView()
SuggestionsBuilder
Returns the legacy string-only builder view.
SuggestionsBuilder legacy = suggestions.legacyView();
legacy.suggest("text");
testFilter(String suggestion)
boolean
Tests whether a suggestion passes the current filter.
suggestion
String
required
The suggestion to test
if (suggestions.testFilter("player")) {
    suggestions.suggest("player");
}
setFilter(Predicate<String> filter)
void
Sets the suggestion filter.
filter
Predicate<String>
The filter predicate, or null to accept all suggestions
suggestions.setFilter(s -> s.startsWith("pl"));

SuggestionTooltip

Creates tooltip metadata for rich suggestions.

Static Factory Methods

SuggestionTooltip.text(String text)
SuggestionTooltip
Creates a plain text tooltip.
text
String
required
The tooltip text
SuggestionTooltip tooltip = SuggestionTooltip.text("Select creative mode");
suggestions.suggest("creative", tooltip);
SuggestionTooltip.handle(Object handle)
SuggestionTooltip
Creates a tooltip backed by a native handle.
handle
Object
required
The native tooltip handle
Component component = Component.text("Tooltip");
SuggestionTooltip tooltip = SuggestionTooltip.handle(component);
SuggestionTooltip.of(Object handle, String text)
SuggestionTooltip
Creates a tooltip with both a native handle and text fallback.
handle
Object
required
The native tooltip handle
text
String
The fallback text representation
Component component = Component.text("Rich tooltip");
SuggestionTooltip tooltip = SuggestionTooltip.of(component, "Rich tooltip");

Instance Methods

text()
String
Returns the tooltip text representation, or null if not provided.
String text = tooltip.text();
as(Class<T> type)
T
Returns a typed native tooltip handle.
type
Class<T>
required
The requested native type
Returns: The native handle, or null if unavailable
Component component = tooltip.as(Component.class);

Usage Examples

Basic String Suggestions

public class MaterialProvider implements ArgumentProvider<Material> {
    @Override
    public Material provide(@NotNull Context ctx, @NotNull InputArgument arg) {
        String value = arg.requireValue().toUpperCase();
        
        try {
            return Material.valueOf(value);
        } catch (IllegalArgumentException e) {
            throw BladeParseError.recoverable(
                "Invalid material: " + value
            );
        }
    }
    
    @Override
    public void suggest(@NotNull Context ctx,
                       @NotNull InputArgument arg,
                       @NotNull SuggestionsBuilder suggestions) {
        // Suggest all materials
        for (Material material : Material.values()) {
            if (material.isItem()) {
                suggestions.suggest(material.name().toLowerCase());
            }
        }
    }
}

Rich Suggestions with Tooltips

public class EnchantmentProvider implements ArgumentProvider<Enchantment> {
    @Override
    public Enchantment provide(@NotNull Context ctx, @NotNull InputArgument arg) {
        String value = arg.requireValue();
        Enchantment enchant = Enchantment.getByKey(NamespacedKey.minecraft(value));
        
        if (enchant == null) {
            throw BladeParseError.recoverable(
                "Unknown enchantment: " + value
            );
        }
        
        return enchant;
    }
    
    @Override
    public void suggestRich(@NotNull Context ctx,
                           @NotNull InputArgument arg,
                           @NotNull RichSuggestionsBuilder suggestions) {
        // Get what user has typed so far
        String typed = suggestions.remainingLowerCase();
        
        // Suggest enchantments with descriptions
        for (Enchantment enchant : Enchantment.values()) {
            String key = enchant.getKey().getKey();
            
            // Filter based on input
            if (key.startsWith(typed)) {
                suggestions.suggest(
                    key,
                    SuggestionTooltip.text(
                        enchant.displayName(1) + " (Max: " + 
                        enchant.getMaxLevel() + ")"
                    )
                );
            }
        }
    }
}

Numeric Suggestions with Range

public class LevelProvider implements ArgumentProvider<Integer> {
    @Override
    public Integer provide(@NotNull Context ctx, @NotNull InputArgument arg) {
        String value = arg.requireValue();
        
        try {
            int level = Integer.parseInt(value);
            
            // Check range
            Range range = arg.range();
            if (range != null) {
                if (level < range.min() || level > range.max()) {
                    throw BladeParseError.recoverable(
                        "Level must be between " + (int)range.min() + 
                        " and " + (int)range.max()
                    );
                }
            }
            
            return level;
        } catch (NumberFormatException e) {
            throw BladeParseError.recoverable(
                "Invalid level: " + value
            );
        }
    }
    
    @Override
    public void suggestRich(@NotNull Context ctx,
                           @NotNull InputArgument arg,
                           @NotNull RichSuggestionsBuilder suggestions) {
        Range range = arg.range();
        
        if (range != null) {
            int min = (int)range.min();
            int max = (int)range.max();
            
            // Suggest levels within range
            for (int i = min; i <= Math.min(max, min + 10); i++) {
                suggestions.suggest(i, 
                    SuggestionTooltip.text("Set level to " + i));
            }
        } else {
            // Suggest common levels
            suggestions.suggest(1, SuggestionTooltip.text("Beginner"));
            suggestions.suggest(10, SuggestionTooltip.text("Intermediate"));
            suggestions.suggest(50, SuggestionTooltip.text("Advanced"));
            suggestions.suggest(100, SuggestionTooltip.text("Expert"));
        }
    }
}

Context-Aware Suggestions

public class PlayerProvider implements ArgumentProvider<Player> {
    @Override
    public Player provide(@NotNull Context ctx, @NotNull InputArgument arg) {
        String value = arg.requireValue();
        Player player = Bukkit.getPlayer(value);
        
        if (player == null) {
            throw BladeParseError.recoverable(
                "Player not found: " + value
            );
        }
        
        return player;
    }
    
    @Override
    public void suggest(@NotNull Context ctx,
                       @NotNull InputArgument arg,
                       @NotNull SuggestionsBuilder suggestions) {
        // Only suggest players visible to sender
        Player sender = ctx.sender().parseAs(Player.class);
        
        for (Player player : Bukkit.getOnlinePlayers()) {
            // Filter based on visibility
            if (sender == null || sender.canSee(player)) {
                suggestions.suggest(player.getName());
            }
        }
    }
}

Conditional Suggestions Based on Previous Arguments

@Command("enchant")
public void enchantCommand(
    Context ctx,
    Player target,
    Enchantment enchantment,
    @Range(min = 1, max = 10) int level
) {
    ItemStack item = target.getInventory().getItemInMainHand();
    item.addEnchantment(enchantment, level);
}

public class EnchantmentLevelProvider implements ArgumentProvider<Integer> {
    @Override
    public void suggestRich(@NotNull Context ctx,
                           @NotNull InputArgument arg,
                           @NotNull RichSuggestionsBuilder suggestions) {
        // Parse the enchantment from previous argument
        Enchantment enchant = ctx.parseArgument(1, Enchantment.class);
        
        if (enchant != null) {
            int maxLevel = enchant.getMaxLevel();
            
            // Suggest valid levels for this enchantment
            for (int i = 1; i <= maxLevel; i++) {
                suggestions.suggest(i,
                    SuggestionTooltip.text("Level " + i + " " + 
                                         enchant.getKey().getKey()));
            }
        } else {
            // Fallback suggestions
            for (int i = 1; i <= 5; i++) {
                suggestions.suggest(i);
            }
        }
    }
}

See Also

Build docs developers (and LLMs) love