Add very simple help utility
This commit is contained in:
parent
c336a2d7e8
commit
756908a3b3
5 changed files with 199 additions and 4 deletions
|
|
@ -0,0 +1,108 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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 com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.arguments.CommandArgument;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public final class CommandHelpHandler<C> {
|
||||||
|
|
||||||
|
private final CommandManager<C> commandManager;
|
||||||
|
|
||||||
|
CommandHelpHandler(@Nonnull final CommandManager<C> commandManager) {
|
||||||
|
this.commandManager = commandManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get exact syntax hints for all commands
|
||||||
|
*
|
||||||
|
* @return Syntax hints for all registered commands, order in lexicographical order
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public List<VerboseHelpEntry<C>> getAllCommands() {
|
||||||
|
final List<VerboseHelpEntry<C>> syntaxHints = new ArrayList<>();
|
||||||
|
for (final Command<C> command : this.commandManager.getCommands()) {
|
||||||
|
final List<CommandArgument<C, ?>> arguments = command.getArguments();
|
||||||
|
syntaxHints.add(new VerboseHelpEntry<>(command,
|
||||||
|
this.commandManager.getCommandSyntaxFormatter().apply(arguments, null)));
|
||||||
|
}
|
||||||
|
syntaxHints.sort(Comparator.comparing(VerboseHelpEntry::getSyntaxString));
|
||||||
|
return syntaxHints;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of the longest shared command chains of all commands.
|
||||||
|
* If there are two commands "foo bar 1" and "foo bar 2", this would
|
||||||
|
* then return "foo bar 1|2"
|
||||||
|
*
|
||||||
|
* @return Longest shared command chains
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public List<String> getLongestSharedChains() {
|
||||||
|
final List<String> chains = new ArrayList<>();
|
||||||
|
this.commandManager.getCommandTree().getRootNodes().forEach(node ->
|
||||||
|
chains.add(node.getValue().getName() + this.commandManager.getCommandSyntaxFormatter()
|
||||||
|
.apply(Collections.emptyList(), node)));
|
||||||
|
chains.sort(String::compareTo);
|
||||||
|
return chains;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final class VerboseHelpEntry<C> {
|
||||||
|
|
||||||
|
private final Command<C> command;
|
||||||
|
private final String syntaxString;
|
||||||
|
|
||||||
|
private VerboseHelpEntry(@Nonnull final Command<C> command, @Nonnull final String syntaxString) {
|
||||||
|
this.command = command;
|
||||||
|
this.syntaxString = syntaxString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the command
|
||||||
|
*
|
||||||
|
* @return Command
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public Command<C> getCommand() {
|
||||||
|
return this.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the syntax string
|
||||||
|
*
|
||||||
|
* @return Syntax string
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public String getSyntaxString() {
|
||||||
|
return this.syntaxString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
//
|
//
|
||||||
package com.intellectualsites.commands;
|
package com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.reflect.TypeToken;
|
import com.google.common.reflect.TypeToken;
|
||||||
import com.intellectualsites.commands.arguments.CommandArgument;
|
import com.intellectualsites.commands.arguments.CommandArgument;
|
||||||
|
|
@ -70,6 +71,7 @@ public abstract class CommandManager<C> {
|
||||||
private final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
private final ServicePipeline servicePipeline = ServicePipeline.builder().build();
|
||||||
private final ParserRegistry<C> parserRegistry = new StandardParserRegistry<>();
|
private final ParserRegistry<C> parserRegistry = new StandardParserRegistry<>();
|
||||||
private final Map<Class<? extends Exception>, BiConsumer<C, ? extends Exception>> exceptionHandlers = Maps.newHashMap();
|
private final Map<Class<? extends Exception>, BiConsumer<C, ? extends Exception>> exceptionHandlers = Maps.newHashMap();
|
||||||
|
private final Collection<Command<C>> commands = Lists.newLinkedList();
|
||||||
|
|
||||||
private final CommandExecutionCoordinator<C> commandExecutionCoordinator;
|
private final CommandExecutionCoordinator<C> commandExecutionCoordinator;
|
||||||
private final CommandTree<C> commandTree;
|
private final CommandTree<C> commandTree;
|
||||||
|
|
@ -166,6 +168,7 @@ public abstract class CommandManager<C> {
|
||||||
*/
|
*/
|
||||||
public CommandManager<C> command(@Nonnull final Command<C> command) {
|
public CommandManager<C> command(@Nonnull final Command<C> command) {
|
||||||
this.commandTree.insertCommand(command);
|
this.commandTree.insertCommand(command);
|
||||||
|
this.commands.add(command);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,6 +201,10 @@ public abstract class CommandManager<C> {
|
||||||
return this.commandRegistrationHandler;
|
return this.commandRegistrationHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final void setCommandRegistrationHandler(@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
|
||||||
|
this.commandRegistrationHandler = commandRegistrationHandler;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the command sender has the required permission. If the permission node is
|
* Check if the command sender has the required permission. If the permission node is
|
||||||
* empty, this should return {@code true}
|
* empty, this should return {@code true}
|
||||||
|
|
@ -389,8 +396,24 @@ public abstract class CommandManager<C> {
|
||||||
Optional.ofNullable(this.getExceptionHandler(clazz)).orElse(defaultHandler).accept(sender, exception);
|
Optional.ofNullable(this.getExceptionHandler(clazz)).orElse(defaultHandler).accept(sender, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void setCommandRegistrationHandler(@Nonnull final CommandRegistrationHandler commandRegistrationHandler) {
|
/**
|
||||||
this.commandRegistrationHandler = commandRegistrationHandler;
|
* Get all registered commands
|
||||||
|
*
|
||||||
|
* @return Unmodifiable view of all registered commands
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public final Collection<Command<C>> getCommands() {
|
||||||
|
return Collections.unmodifiableCollection(this.commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a command help handler instance
|
||||||
|
*
|
||||||
|
* @return Command help handler
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public final CommandHelpHandler<C> getCommandHelpHandler() {
|
||||||
|
return new CommandHelpHandler<>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ package com.intellectualsites.commands.arguments;
|
||||||
import com.intellectualsites.commands.CommandTree;
|
import com.intellectualsites.commands.CommandTree;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,6 +46,6 @@ public interface CommandSyntaxFormatter<C> {
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
String apply(@Nonnull List<CommandArgument<C, ?>> commandArguments,
|
String apply(@Nonnull List<CommandArgument<C, ?>> commandArguments,
|
||||||
@Nonnull CommandTree.Node<CommandArgument<C, ?>> node);
|
@Nullable CommandTree.Node<CommandArgument<C, ?>> node);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ package com.intellectualsites.commands.arguments;
|
||||||
import com.intellectualsites.commands.CommandTree;
|
import com.intellectualsites.commands.CommandTree;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ public class StandardCommandSyntaxFormatter<C> implements CommandSyntaxFormatter
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Override
|
@Override
|
||||||
public final String apply(@Nonnull final List<CommandArgument<C, ?>> commandArguments,
|
public final String apply(@Nonnull final List<CommandArgument<C, ?>> commandArguments,
|
||||||
@Nonnull final CommandTree.Node<CommandArgument<C, ?>> node) {
|
@Nullable final CommandTree.Node<CommandArgument<C, ?>> node) {
|
||||||
final StringBuilder stringBuilder = new StringBuilder();
|
final StringBuilder stringBuilder = new StringBuilder();
|
||||||
final Iterator<CommandArgument<C, ?>> iterator = commandArguments.iterator();
|
final Iterator<CommandArgument<C, ?>> iterator = commandArguments.iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
//
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Copyright (c) 2020 Alexander Söderberg
|
||||||
|
//
|
||||||
|
// 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 com.intellectualsites.commands;
|
||||||
|
|
||||||
|
import com.intellectualsites.commands.arguments.standard.IntegerArgument;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
class CommandHelpHandlerTest {
|
||||||
|
|
||||||
|
private static CommandManager<TestCommandSender> manager;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void setup() {
|
||||||
|
manager = new TestCommandManager();
|
||||||
|
manager.command(manager.commandBuilder("test").literal("this").literal("thing").build());
|
||||||
|
manager.command(manager.commandBuilder("test").literal("int").
|
||||||
|
argument(IntegerArgument.required("int")).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testVerboseHelp() {
|
||||||
|
final List<CommandHelpHandler.VerboseHelpEntry<TestCommandSender>> syntaxHints
|
||||||
|
= manager.getCommandHelpHandler().getAllCommands();
|
||||||
|
final CommandHelpHandler.VerboseHelpEntry<TestCommandSender> entry1 = syntaxHints.get(0);
|
||||||
|
Assertions.assertEquals("test int <int>", entry1.getSyntaxString());
|
||||||
|
final CommandHelpHandler.VerboseHelpEntry<TestCommandSender> entry2 = syntaxHints.get(1);
|
||||||
|
Assertions.assertEquals("test this thing", entry2.getSyntaxString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLongestChains() {
|
||||||
|
final List<String> longestChains = manager.getCommandHelpHandler().getLongestSharedChains();
|
||||||
|
Assertions.assertEquals(Arrays.asList("test int|this"), longestChains);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue