mirror of
https://github.com/aikar/commands.git
synced 2026-06-22 23:00:36 +00:00
Current work on command searching for help, see image
http://i.imgur.com/HQ6nmvF.png @Default @Subcommand("help") @UnknownHandler public void doHelp(CommandSender sender, CommandHelp help) { help.showHelp(); }
This commit is contained in:
@@ -255,6 +255,7 @@ public abstract class BaseCommand {
|
||||
for (String subcmd : cmdList) {
|
||||
subCommands.put(subcmd, cmd);
|
||||
}
|
||||
cmd.addSubcommands(cmdList);
|
||||
|
||||
if (aliasNames != null) {
|
||||
for (String name : aliasNames) {
|
||||
|
||||
@@ -148,15 +148,22 @@ public class CommandContexts <R extends CommandExecutionContext<?, ? extends Com
|
||||
});
|
||||
registerOptionalContext(CommandHelp.class, (c) -> {
|
||||
String first = c.getFirstArg();
|
||||
String last = c.getLastArg();
|
||||
int page = 1;
|
||||
List<String> search = null;
|
||||
if (first != null && ACFUtil.isInteger(first)) {
|
||||
if (last != null && ACFUtil.isInteger(last)) {
|
||||
c.popLastArg();
|
||||
page = ACFUtil.parseInt(last);
|
||||
if (!c.getArgs().isEmpty()) {
|
||||
search = c.getArgs();
|
||||
}
|
||||
} else if (first != null && ACFUtil.isInteger(first)) {
|
||||
c.popFirstArg();
|
||||
page = ACFUtil.parseInt(first);
|
||||
if (!c.getArgs().isEmpty()) {
|
||||
search = c.getArgs();
|
||||
}
|
||||
} else if (first != null && !c.getArgs().isEmpty()) {
|
||||
} else if (!c.getArgs().isEmpty()) {
|
||||
search = c.getArgs();
|
||||
}
|
||||
CommandHelp commandHelp = manager.generateCommandHelp();
|
||||
|
||||
@@ -70,10 +70,18 @@ public class CommandExecutionContext <T extends CommandExecutionContext, I exten
|
||||
return !args.isEmpty() ? args.remove(0) : null;
|
||||
}
|
||||
|
||||
public String popLastArg() {
|
||||
return !args.isEmpty() ? args.remove(args.size() - 1) : null;
|
||||
}
|
||||
|
||||
public String getFirstArg() {
|
||||
return !args.isEmpty() ? args.get(0) : null;
|
||||
}
|
||||
|
||||
public String getLastArg() {
|
||||
return !args.isEmpty() ? args.get(args.size() - 1) : null;
|
||||
}
|
||||
|
||||
public boolean isLastArg() {
|
||||
return cmd.parameters.length -1 == index;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class CommandHelp {
|
||||
@@ -58,21 +59,38 @@ public class CommandHelp {
|
||||
});
|
||||
}
|
||||
|
||||
private boolean matchesSearch(HelpEntry help) {
|
||||
if (this.search == null) {
|
||||
return true;
|
||||
@UnstableAPI // Not sure on this one yet even when API becomes unstable
|
||||
protected void updateSearchScore(HelpEntry help) {
|
||||
if (this.search == null || this.search.isEmpty()) {
|
||||
help.setSearchScore(1);
|
||||
return;
|
||||
}
|
||||
final RegisteredCommand cmd = help.getRegisteredCommand();
|
||||
final RegisteredCommand<?> cmd = help.getRegisteredCommand();
|
||||
|
||||
int searchScore = 0;
|
||||
for (String word : this.search) {
|
||||
Pattern pattern = Pattern.compile(Pattern.quote(word));
|
||||
if (pattern.matcher(cmd.command).matches()) {
|
||||
return true;
|
||||
} else if (pattern.matcher(help.getDescription()).matches()) {
|
||||
return true;
|
||||
Pattern pattern = Pattern.compile(".*" + Pattern.quote(word) + ".*", Pattern.CASE_INSENSITIVE);
|
||||
for (String subCmd : cmd.registeredSubcommands) {
|
||||
Pattern subCmdPattern = Pattern.compile(".*" + Pattern.quote(subCmd) + ".*", Pattern.CASE_INSENSITIVE);
|
||||
if (pattern.matcher(subCmd).matches()) {
|
||||
searchScore += 3;
|
||||
} else if (subCmdPattern.matcher(word).matches()) {
|
||||
searchScore++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (pattern.matcher(help.getDescription()).matches()) {
|
||||
searchScore += 2;
|
||||
}
|
||||
if (pattern.matcher(help.getParameterSyntax()).matches()) {
|
||||
searchScore++;
|
||||
}
|
||||
if (help.getSearchTags() != null && pattern.matcher(help.getSearchTags()).matches()) {
|
||||
searchScore += 2;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
help.setSearchScore(searchScore);
|
||||
}
|
||||
|
||||
public CommandManager getManager() {
|
||||
@@ -88,15 +106,21 @@ public class CommandHelp {
|
||||
}
|
||||
|
||||
public void showHelp(CommandIssuer issuer, MessageKeyProvider format) {
|
||||
getHelpEntries().forEach(e -> {
|
||||
if (!matchesSearch(e)) {
|
||||
return;
|
||||
}
|
||||
Iterator<HelpEntry> results = getHelpEntries().stream()
|
||||
.filter(HelpEntry::shouldShow)
|
||||
.sorted(Comparator.comparingInt(helpEntry -> helpEntry.getSearchScore() * -1)).iterator();
|
||||
if (!results.hasNext()) {
|
||||
issuer.sendMessage(MessageType.ERROR, MessageKeys.NO_COMMAND_MATCHED_SEARCH, "{search}", ACFUtil.join(this.search, " "));
|
||||
results = getHelpEntries().iterator();
|
||||
}
|
||||
|
||||
while (results.hasNext()) {
|
||||
HelpEntry e = results.next();
|
||||
String formatted = this.manager.formatMessage(issuer, MessageType.HELP, format, getFormatReplacements(e));
|
||||
for (String msg : ACFPatterns.NEWLINE.split(formatted)) {
|
||||
issuer.sendMessageInternal(ACFUtil.rtrim(msg));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,5 +149,6 @@ public class CommandHelp {
|
||||
|
||||
public void setSearch(List<String> search) {
|
||||
this.search = search;
|
||||
getHelpEntries().forEach(this::updateSearchScore);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,18 @@
|
||||
|
||||
package co.aikar.commands;
|
||||
|
||||
import co.aikar.commands.annotation.HelpSearchTags;
|
||||
|
||||
public class HelpEntry {
|
||||
|
||||
private final RegisteredCommand command;
|
||||
private final String searchTags;
|
||||
private int searchScore = 1;
|
||||
|
||||
HelpEntry(RegisteredCommand command) {
|
||||
this.command = command;
|
||||
HelpSearchTags tagsAnno = command.method.getAnnotation(HelpSearchTags.class);
|
||||
this.searchTags = tagsAnno != null ? tagsAnno.value() : null;
|
||||
}
|
||||
|
||||
RegisteredCommand getRegisteredCommand() {
|
||||
@@ -47,4 +53,20 @@ public class HelpEntry {
|
||||
public String getDescription(){
|
||||
return this.command.helpText;
|
||||
}
|
||||
|
||||
public void setSearchScore(int searchScore) {
|
||||
this.searchScore = searchScore;
|
||||
}
|
||||
|
||||
public boolean shouldShow() {
|
||||
return this.searchScore > 0;
|
||||
}
|
||||
|
||||
public int getSearchScore() {
|
||||
return searchScore;
|
||||
}
|
||||
|
||||
public String getSearchTags() {
|
||||
return searchTags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@ public enum MessageKeys implements MessageKeyProvider {
|
||||
MUST_BE_MAX_LENGTH,
|
||||
NOT_ALLOWED_ON_CONSOLE,
|
||||
COULD_NOT_FIND_PLAYER,
|
||||
HELP_FORMAT;
|
||||
HELP_FORMAT,
|
||||
NO_COMMAND_MATCHED_SEARCH;
|
||||
|
||||
private final MessageKey key = MessageKey.of("acf-core." + this.name().toLowerCase());
|
||||
public MessageKey getMessageKey() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import co.aikar.commands.annotation.*;
|
||||
import co.aikar.commands.contexts.ContextResolver;
|
||||
import co.aikar.commands.contexts.IssuerAwareContextResolver;
|
||||
import co.aikar.commands.contexts.IssuerOnlyContextResolver;
|
||||
import co.aikar.commands.contexts.OptionalContextResolver;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
@@ -35,6 +36,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -54,6 +57,7 @@ public class RegisteredCommand <R extends CommandExecutionContext<? extends Comm
|
||||
final String complete;
|
||||
final int requiredResolvers;
|
||||
final int optionalResolvers;
|
||||
final List<String> registeredSubcommands = new ArrayList<>();
|
||||
|
||||
RegisteredCommand(BaseCommand scope, String command, Method method, String prefSubCommand) {
|
||||
this.scope = scope;
|
||||
@@ -116,13 +120,14 @@ public class RegisteredCommand <R extends CommandExecutionContext<? extends Comm
|
||||
}
|
||||
|
||||
private boolean isOptionalResolver(ContextResolver<?, R> resolver, Parameter parameter) {
|
||||
return resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver
|
||||
return isOptionalResolver(resolver)
|
||||
|| parameter.getAnnotation(Optional.class) != null
|
||||
|| parameter.getAnnotation(Default.class) != null;
|
||||
}
|
||||
|
||||
private boolean isSenderAwareResolver(ContextResolver<?, R> resolver) {
|
||||
return resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver;
|
||||
private boolean isOptionalResolver(ContextResolver<?, R> resolver) {
|
||||
return resolver instanceof IssuerAwareContextResolver || resolver instanceof IssuerOnlyContextResolver
|
||||
|| resolver instanceof OptionalContextResolver;
|
||||
}
|
||||
|
||||
void invoke(CommandIssuer sender, List<String> args) {
|
||||
@@ -195,7 +200,7 @@ public class RegisteredCommand <R extends CommandExecutionContext<? extends Comm
|
||||
if (allowOptional && def != null) {
|
||||
args.add(scope.manager.getCommandReplacements().replace(def.value()));
|
||||
} else if (allowOptional && opt != null) {
|
||||
passedArgs.put(parameterName, isSenderAwareResolver(resolver) ? resolver.getContext(context) : null);
|
||||
passedArgs.put(parameterName, isOptionalResolver(resolver) ? resolver.getContext(context) : null);
|
||||
//noinspection UnnecessaryContinue
|
||||
continue;
|
||||
} else if (!isOptionalResolver) {
|
||||
@@ -247,4 +252,11 @@ public class RegisteredCommand <R extends CommandExecutionContext<? extends Comm
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public void addSubcommand(String cmd) {
|
||||
this.registeredSubcommands.add(cmd);
|
||||
}
|
||||
public void addSubcommands(Collection<String> cmd) {
|
||||
this.registeredSubcommands.addAll(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
|
||||
*
|
||||
* 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 co.aikar.commands.annotation;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface HelpSearchTags {
|
||||
String value();
|
||||
}
|
||||
@@ -34,3 +34,4 @@ acf-core.must_be_max_length = Error: Must be less than {max} characters long.
|
||||
acf-core.not_allowed_on_console = Error: Console may not execute this command.
|
||||
acf-core.could_not_find_player = Error: Could not find a player by the name: <c2>{search}</c2>
|
||||
acf-core.help_format = <c1>{command}</c1> <c2>{parameters}</c2> <c3>{seperator} {description}</c3>
|
||||
acf-core.no_command_matched_search = No command matched <c2>{search}</c2>.
|
||||
|
||||
Reference in New Issue
Block a user