Support repeating literals or argument names (#168)

Signed-off-by: Irmo van den Berge <irmo.vandenberge@ziggo.nl>
This commit is contained in:
Irmo van den Berge 2020-12-16 12:31:30 +01:00 committed by Alexander Söderberg
parent 1e91273e0e
commit 5224050c99
5 changed files with 330 additions and 53 deletions

View file

@ -43,13 +43,13 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* A command consists out of a chain of {@link CommandArgument command arguments}.
@ -58,7 +58,8 @@ import java.util.function.Consumer;
*/
public class Command<C> {
private final Map<@NonNull CommandArgument<C, ?>, @NonNull Description> arguments;
private final List<@NonNull CommandComponent<C>> components;
private final List<@NonNull CommandArgument<C, ?>> arguments;
private final CommandExecutionHandler<C> commandExecutionHandler;
private final Class<? extends C> senderType;
private final CommandPermission commandPermission;
@ -67,26 +68,27 @@ public class Command<C> {
/**
* Construct a new command
*
* @param commandArguments Command argument and description pairs
* @param commandComponents Command component argument and description
* @param commandExecutionHandler Execution handler
* @param senderType Required sender type. May be {@code null}
* @param commandPermission Command permission
* @param commandMeta Command meta instance
*/
public Command(
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @Nullable Class<? extends C> senderType,
final @NonNull CommandPermission commandPermission,
final @NonNull CommandMeta commandMeta
) {
this.arguments = Objects.requireNonNull(commandArguments, "Command arguments may not be null");
if (this.arguments.size() == 0) {
throw new IllegalArgumentException("At least one command argument is required");
this.components = Objects.requireNonNull(commandComponents, "Command components may not be null");
this.arguments = this.components.stream().map(CommandComponent::getArgument).collect(Collectors.toList());
if (this.components.isEmpty()) {
throw new IllegalArgumentException("At least one command component is required");
}
// Enforce ordering of command arguments
boolean foundOptional = false;
for (final CommandArgument<C, ?> argument : this.arguments.keySet()) {
for (final CommandArgument<C, ?> argument : this.arguments) {
if (argument.getName().isEmpty()) {
throw new IllegalArgumentException("Argument names may not be empty");
}
@ -109,18 +111,77 @@ public class Command<C> {
/**
* Construct a new command
*
* @param commandArguments Command arguments
* @param commandComponents Command components
* @param commandExecutionHandler Execution handler
* @param senderType Required sender type. May be {@code null}
* @param commandMeta Command meta instance
*/
public Command(
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @Nullable Class<? extends C> senderType,
final @NonNull CommandMeta commandMeta
) {
this(commandComponents, commandExecutionHandler, senderType, Permission.empty(), commandMeta);
}
/**
* Construct a new command
*
* @param commandComponents Command components
* @param commandExecutionHandler Execution handler
* @param commandPermission Command permission
* @param commandMeta Command meta instance
*/
public Command(
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @NonNull CommandPermission commandPermission,
final @NonNull CommandMeta commandMeta
) {
this(commandComponents, commandExecutionHandler, null, commandPermission, commandMeta);
}
/**
* Construct a new command
*
* @param commandArguments Command argument and description pairs
* @param commandExecutionHandler Execution handler
* @param senderType Required sender type. May be {@code null}
* @param commandPermission Command permission
* @param commandMeta Command meta instance
* @deprecated Map does not allow for the same literal or variable argument name to repeat
* @see #Command(List, CommandExecutionHandler, Class, CommandPermission, CommandMeta)
*/
@Deprecated
public Command(
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @Nullable Class<? extends C> senderType,
final @NonNull CommandPermission commandPermission,
final @NonNull CommandMeta commandMeta
) {
this(mapToComponents(commandArguments), commandExecutionHandler, senderType, commandPermission, commandMeta);
}
/**
* Construct a new command
*
* @param commandArguments Command arguments
* @param commandExecutionHandler Execution handler
* @param senderType Required sender type. May be {@code null}
* @param commandMeta Command meta instance
* @deprecated Map does not allow for the same literal or variable argument name to repeat
* @see #Command(List, CommandExecutionHandler, Class, CommandMeta)
*/
@Deprecated
public Command(
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @Nullable Class<? extends C> senderType,
final @NonNull CommandMeta commandMeta
) {
this(commandArguments, commandExecutionHandler, senderType, Permission.empty(), commandMeta);
this(mapToComponents(commandArguments), commandExecutionHandler, senderType, commandMeta);
}
/**
@ -130,14 +191,27 @@ public class Command<C> {
* @param commandExecutionHandler Execution handler
* @param commandPermission Command permission
* @param commandMeta Command meta instance
* @deprecated Map does not allow for the same literal or variable argument name to repeat
* @see #Command(List, CommandExecutionHandler, CommandPermission, CommandMeta)
*/
@Deprecated
public Command(
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @NonNull CommandPermission commandPermission,
final @NonNull CommandMeta commandMeta
) {
this(commandArguments, commandExecutionHandler, null, commandPermission, commandMeta);
this(mapToComponents(commandArguments), commandExecutionHandler, commandPermission, commandMeta);
}
// Converts a map of CommandArgument and Description pairs to a List of CommandComponent
// Used for backwards-compatibility
private static <C> @NonNull List<@NonNull CommandComponent<C>> mapToComponents(
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments
) {
return commandArguments.entrySet().stream()
.map(e -> CommandComponent.of(e.getKey(), e.getValue()))
.collect(Collectors.toList());
}
/**
@ -157,13 +231,13 @@ public class Command<C> {
final @NonNull Description description,
final @NonNull String... aliases
) {
final Map<@NonNull CommandArgument<C, ?>, @NonNull Description> map = new LinkedHashMap<>();
map.put(StaticArgument.of(commandName, aliases), description);
final List<CommandComponent<C>> commands = new ArrayList<>();
commands.add(CommandComponent.of(StaticArgument.of(commandName, aliases), description));
return new Builder<>(
null,
commandMeta,
null,
map,
commands,
new CommandExecutionHandler.NullCommandExecutionHandler<>(),
Permission.empty(),
Collections.emptyList()
@ -185,13 +259,13 @@ public class Command<C> {
final @NonNull CommandMeta commandMeta,
final @NonNull String... aliases
) {
final Map<CommandArgument<C, ?>, Description> map = new LinkedHashMap<>();
map.put(StaticArgument.of(commandName, aliases), Description.empty());
final List<CommandComponent<C>> commands = new ArrayList<>();
commands.add(CommandComponent.of(StaticArgument.of(commandName, aliases), Description.empty()));
return new Builder<>(
null,
commandMeta,
null,
map,
commands,
new CommandExecutionHandler.NullCommandExecutionHandler<>(),
Permission.empty(),
Collections.emptyList()
@ -201,10 +275,19 @@ public class Command<C> {
/**
* Return a copy of the command argument array
*
* @return Copy of the command argument array
* @return Copy of the command argument array. This List is mutable.
*/
public @NonNull List<CommandArgument<@NonNull C, @NonNull ?>> getArguments() {
return new ArrayList<>(this.arguments.keySet());
return new ArrayList<>(this.arguments);
}
/**
* Returns a copy of the command component array
*
* @return Copy of the command component array. This List is mutable.
*/
public @NonNull List<CommandComponent<@NonNull C>> getComponents() {
return new ArrayList<>(this.components);
}
/**
@ -248,9 +331,18 @@ public class Command<C> {
*
* @param argument Argument
* @return Argument description
* @throws IllegalArgumentException If the command argument does not exist
* @deprecated More than one matching command argument may exist per command.
* Use {@link #getArguments()} and search in that, instead.
*/
@Deprecated
public @NonNull String getArgumentDescription(final @NonNull CommandArgument<C, ?> argument) {
return this.arguments.get(argument).getDescription();
for (final CommandComponent<C> component : this.components) {
if (component.getArgument().equals(argument)) {
return component.getDescription().getDescription();
}
}
throw new IllegalArgumentException("Command argument not found: " + argument);
}
@Override
@ -282,7 +374,7 @@ public class Command<C> {
public static final class Builder<C> {
private final CommandMeta commandMeta;
private final Map<CommandArgument<C, ?>, Description> commandArguments;
private final List<CommandComponent<C>> commandComponents;
private final CommandExecutionHandler<C> commandExecutionHandler;
private final Class<? extends C> senderType;
private final CommandPermission commandPermission;
@ -293,14 +385,14 @@ public class Command<C> {
final @Nullable CommandManager<C> commandManager,
final @NonNull CommandMeta commandMeta,
final @Nullable Class<? extends C> senderType,
final @NonNull Map<@NonNull CommandArgument<C, ?>, @NonNull Description> commandArguments,
final @NonNull List<@NonNull CommandComponent<C>> commandComponents,
final @NonNull CommandExecutionHandler<@NonNull C> commandExecutionHandler,
final @NonNull CommandPermission commandPermission,
final @NonNull Collection<CommandFlag<?>> flags
) {
this.commandManager = commandManager;
this.senderType = senderType;
this.commandArguments = Objects.requireNonNull(commandArguments, "Arguments may not be null");
this.commandComponents = Objects.requireNonNull(commandComponents, "Components may not be null");
this.commandExecutionHandler = Objects.requireNonNull(commandExecutionHandler, "Execution handler may not be null");
this.commandPermission = Objects.requireNonNull(commandPermission, "Permission may not be null");
this.commandMeta = Objects.requireNonNull(commandMeta, "Meta may not be null");
@ -346,7 +438,7 @@ public class Command<C> {
this.commandManager,
commandMeta,
this.senderType,
this.commandArguments,
this.commandComponents,
this.commandExecutionHandler,
this.commandPermission,
this.flags
@ -388,7 +480,7 @@ public class Command<C> {
commandManager,
this.commandMeta,
this.senderType,
this.commandArguments,
this.commandComponents,
this.commandExecutionHandler,
this.commandPermission,
this.flags
@ -465,13 +557,13 @@ public class Command<C> {
+ " Use CommandArgument#copy to create a copy of the argument.");
}
argument.setArgumentRegistered();
final Map<CommandArgument<C, ?>, Description> commandArgumentMap = new LinkedHashMap<>(this.commandArguments);
commandArgumentMap.put(argument, description);
final List<CommandComponent<C>> commandComponents = new ArrayList<>(this.commandComponents);
commandComponents.add(CommandComponent.of(argument, description));
return new Builder<>(
this.commandManager,
this.commandMeta,
this.senderType,
commandArgumentMap,
commandComponents,
this.commandExecutionHandler,
this.commandPermission,
this.flags
@ -491,13 +583,13 @@ public class Command<C> {
final CommandArgument.@NonNull Builder<C, T> builder,
final @NonNull Description description
) {
final Map<CommandArgument<C, ?>, Description> commandArgumentMap = new LinkedHashMap<>(this.commandArguments);
commandArgumentMap.put(builder.build(), description);
final List<CommandComponent<C>> commandComponents = new ArrayList<>(this.commandComponents);
commandComponents.add(CommandComponent.of(builder.build(), description));
return new Builder<>(
this.commandManager,
this.commandMeta,
this.senderType,
commandArgumentMap,
commandComponents,
this.commandExecutionHandler,
this.commandPermission,
this.flags
@ -676,7 +768,7 @@ public class Command<C> {
this.commandManager,
this.commandMeta,
this.senderType,
this.commandArguments,
this.commandComponents,
commandExecutionHandler,
this.commandPermission,
this.flags
@ -694,7 +786,7 @@ public class Command<C> {
this.commandManager,
this.commandMeta,
senderType,
this.commandArguments,
this.commandComponents,
this.commandExecutionHandler,
this.commandPermission,
this.flags
@ -712,7 +804,7 @@ public class Command<C> {
this.commandManager,
this.commandMeta,
this.senderType,
this.commandArguments,
this.commandComponents,
this.commandExecutionHandler,
permission,
this.flags
@ -730,7 +822,7 @@ public class Command<C> {
this.commandManager,
this.commandMeta,
this.senderType,
this.commandArguments,
this.commandComponents,
this.commandExecutionHandler,
Permission.of(permission),
this.flags
@ -750,12 +842,13 @@ public class Command<C> {
*/
public @NonNull Builder<C> proxies(final @NonNull Command<C> command) {
Builder<C> builder = this;
for (final CommandArgument<C, ?> argument : command.getArguments()) {
for (final CommandComponent<C> component : command.getComponents()) {
final CommandArgument<C, ?> argument = component.getArgument();
if (argument instanceof StaticArgument) {
continue;
}
final CommandArgument<C, ?> builtArgument = argument.copy();
builder = builder.argument(builtArgument, Description.of(command.getArgumentDescription(argument)));
builder = builder.argument(builtArgument, component.getDescription());
}
if (this.commandPermission.toString().isEmpty()) {
builder = builder.permission(command.getCommandPermission());
@ -787,7 +880,7 @@ public class Command<C> {
this.commandManager,
this.commandMeta,
this.senderType,
this.commandArguments,
this.commandComponents,
this.commandExecutionHandler,
this.commandPermission,
Collections.unmodifiableList(flags)
@ -811,14 +904,14 @@ public class Command<C> {
* @return Built command
*/
public @NonNull Command<C> build() {
final LinkedHashMap<CommandArgument<C, ?>, Description> commandArguments = new LinkedHashMap<>(this.commandArguments);
final List<CommandComponent<C>> commandComponents = new ArrayList<>(this.commandComponents);
/* Construct flag node */
if (!flags.isEmpty()) {
final FlagArgument<C> flagArgument = new FlagArgument<>(this.flags);
commandArguments.put(flagArgument, Description.of("Command flags"));
commandComponents.add(CommandComponent.of(flagArgument, Description.of("Command flags")));
}
return new Command<>(
Collections.unmodifiableMap(commandArguments),
Collections.unmodifiableList(commandComponents),
this.commandExecutionHandler,
this.senderType,
this.commandPermission,

View file

@ -0,0 +1,111 @@
//
// MIT License
//
// Copyright (c) 2020 Alexander Söderberg & Contributors
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
package cloud.commandframework;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.NonNull;
import cloud.commandframework.arguments.CommandArgument;
/**
* A single literal or argument component of a command
*
* @param <C> Command sender type
*/
public final class CommandComponent<C> {
private final CommandArgument<C, ?> argument;
private final Description description;
/**
* Initializes a new CommandComponent
*
* @param commandArgument Command Component Argument
* @param commandDescription Command Component Description
*/
private CommandComponent(
final @NonNull CommandArgument<C, ?> commandArgument,
final @NonNull Description commandDescription
) {
this.argument = commandArgument;
this.description = commandDescription;
}
/**
* Gets the command component argument details
*
* @return command component argument details
*/
public @NonNull CommandArgument<C, ?> getArgument() {
return this.argument;
}
/**
* Gets the command component description
*
* @return command component description
*/
public @NonNull Description getDescription() {
return this.description;
}
@Override
public int hashCode() {
return Objects.hash(getArgument(), getDescription());
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
} else if (o instanceof CommandComponent) {
CommandComponent<?> that = (CommandComponent<?>) o;
return getArgument().equals(that.getArgument())
&& getDescription().equals(that.getDescription());
} else {
return false;
}
}
@Override
public @NonNull String toString() {
return String.format("%s{argument=%s,description=%s}", this.getClass().getSimpleName(),
this.argument, this.description);
}
/**
* Creates a new CommandComponent with the provided argument and description
*
* @param <C> Command sender type
* @param commandArgument Command Component Argument
* @param commandDescription Command Component Description
* @return new CommandComponent
*/
public static <C> @NonNull CommandComponent<C> of(
final @NonNull CommandArgument<C, ?> commandArgument,
final @NonNull Description commandDescription
) {
return new CommandComponent<C>(commandArgument, commandDescription);
}
}

View file

@ -23,7 +23,6 @@
//
package cloud.commandframework;
import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.standard.IntegerArgument;
import cloud.commandframework.meta.CommandMeta;
import cloud.commandframework.meta.SimpleCommandMeta;
@ -142,17 +141,17 @@ class CommandHelpHandlerTest {
.apply(helpTopic.getCommand().getArguments(), null));
System.out.printf(" ├── Description: %s\n", helpTopic.getDescription());
System.out.println(" └── Args: ");
final Iterator<CommandArgument<TestCommandSender, ?>> iterator = helpTopic.getCommand().getArguments().iterator();
final Iterator<CommandComponent<TestCommandSender>> iterator = helpTopic.getCommand().getComponents().iterator();
while (iterator.hasNext()) {
final CommandArgument<TestCommandSender, ?> argument = iterator.next();
final CommandComponent<TestCommandSender> component = iterator.next();
String description = helpTopic.getCommand().getArgumentDescription(argument);
String description = component.getDescription().getDescription();
if (!description.isEmpty()) {
description = ": " + description;
}
System.out.printf(" %s %s%s\n", iterator.hasNext() ? "├──" : "└──", manager.getCommandSyntaxFormatter().apply(
Collections.singletonList(argument), null), description);
Collections.singletonList(component.getArgument()), null), description);
}
}

View file

@ -35,6 +35,8 @@ import cloud.commandframework.exceptions.AmbiguousNodeException;
import cloud.commandframework.exceptions.NoPermissionException;
import cloud.commandframework.meta.SimpleCommandMeta;
import cloud.commandframework.types.tuples.Pair;
import io.leangen.geantyref.TypeToken;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -42,6 +44,7 @@ import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CompletionException;
class CommandTreeTest {
@ -220,6 +223,45 @@ class CommandTreeTest {
));
}
@Test
void getArgumentsAndComponents() {
// Create and register a command
Command<TestCommandSender> command = manager.commandBuilder("component")
.literal("literal", "literalalias")
.literal("detail", Description.of("detaildescription"))
.argument(CommandArgument.ofType(int.class, "argument"),
Description.of("argumentdescription"))
.build();
manager.command(command);
// Verify all the details we have configured are present
List<CommandArgument<TestCommandSender, ?>> arguments = command.getArguments();
List<CommandComponent<TestCommandSender>> components = command.getComponents();
Assertions.assertEquals(arguments.size(), components.size());
Assertions.assertEquals(4, components.size());
// Arguments should exactly match the component getArgument() result, in same order
for (int i = 0; i < components.size(); i++) {
Assertions.assertEquals(components.get(i).getArgument(), arguments.get(i));
}
// Argument configuration, we know component has the same argument so no need to test those
// TODO: Aliases
Assertions.assertEquals("component", arguments.get(0).getName());
Assertions.assertEquals("literal", arguments.get(1).getName());
Assertions.assertEquals("detail", arguments.get(2).getName());
Assertions.assertEquals("argument", arguments.get(3).getName());
// Check argument is indeed a command argument
Assertions.assertEquals(TypeToken.get(int.class), arguments.get(3).getValueType());
// Check description is set for all components, is empty when not specified
Assertions.assertEquals("", components.get(0).getDescription().getDescription());
Assertions.assertEquals("", components.get(1).getDescription().getDescription());
Assertions.assertEquals("detaildescription", components.get(2).getDescription().getDescription());
Assertions.assertEquals("argumentdescription", components.get(3).getDescription().getDescription());
}
@Test
void getSuggestions() {
Assertions.assertFalse(
@ -319,6 +361,36 @@ class CommandTreeTest {
newTree();
}
@Test
void testLiteralRepeatingArgument() {
// Build a command with a literal repeating
Command<TestCommandSender> command = manager.commandBuilder("repeatingargscommand")
.literal("repeat")
.literal("middle")
.literal("repeat")
.build();
// Verify built command has the repeat argument twice
List<CommandArgument<TestCommandSender, ?>> args = command.getArguments();
Assertions.assertEquals(4, args.size());
Assertions.assertEquals("repeatingargscommand", args.get(0).getName());
Assertions.assertEquals("repeat", args.get(1).getName());
Assertions.assertEquals("middle", args.get(2).getName());
Assertions.assertEquals("repeat", args.get(3).getName());
// Register
manager.command(command);
// If internally it drops repeating arguments, then it would register:
// > /repeatingargscommand repeat middle
// So check that we can register that exact command without an ambiguity exception
manager.command(
manager.commandBuilder("repeatingargscommand")
.literal("repeat")
.literal("middle")
);
}
@Test
void testDuplicateArgument() {
final CommandArgument<TestCommandSender, String> argument = StringArgument.of("test");

View file

@ -23,6 +23,7 @@
//
package cloud.commandframework.minecraft.extras;
import cloud.commandframework.CommandComponent;
import cloud.commandframework.CommandHelpHandler;
import cloud.commandframework.CommandManager;
import cloud.commandframework.arguments.CommandArgument;
@ -391,34 +392,35 @@ public final class MinecraftHelp<C> {
))
);
final Iterator<CommandArgument<C, ?>> iterator = helpTopic.getCommand().getArguments().iterator();
final Iterator<CommandComponent<C>> iterator = helpTopic.getCommand().getComponents().iterator();
/* Skip the first one because it's the command literal */
iterator.next();
while (iterator.hasNext()) {
final CommandArgument<C, ?> argument = iterator.next();
final CommandComponent<C> component = iterator.next();
final CommandArgument<C, ?> argument = component.getArgument();
String syntax = this.commandManager.getCommandSyntaxFormatter()
.apply(Collections.singletonList(argument), null);
final TextComponent.Builder component = Component.text()
final TextComponent.Builder textComponent = Component.text()
.append(Component.text(" "))
.append(iterator.hasNext() ? this.branch() : this.lastBranch())
.append(this.highlight(Component.text(" " + syntax, this.colors.highlight)));
if (!argument.isRequired()) {
component.append(Component.text(
textComponent.append(Component.text(
" (" + this.messageProvider.apply(sender, MESSAGE_OPTIONAL) + ")",
this.colors.alternateHighlight
));
}
final String description = helpTopic.getCommand().getArgumentDescription(argument);
final String description = component.getDescription().getDescription();
if (!description.isEmpty()) {
component
textComponent
.append(Component.text(" - ", this.colors.accent))
.append(Component.text(description, this.colors.text));
}
audience.sendMessage(Identity.nil(), component);
audience.sendMessage(Identity.nil(), textComponent);
}
}
audience.sendMessage(Identity.nil(), this.footer(sender));