Made better cmd error msgs.

ArAbstractSelect will euggest a matching option if,
an invalid arg was passed & a matching one exists at.
If an invalid subcommand aliases were passed,
a matching subcommand will be suggested.
After feedback from MonMarty a few misc messages was changed.
This commit is contained in:
Magnus Ulf 2015-03-02 17:08:11 +01:00 committed by Olof Larsson
parent f3ad2a40ba
commit 6176369f35
3 changed files with 142 additions and 39 deletions

View File

@ -7,7 +7,10 @@ public class Lang
public static final String COMMAND_SENDER_MUST_BE_PLAYER = "<b>This command can only be used by ingame players.";
public static final String COMMAND_SENDER_MUSNT_BE_PLAYER = "<b>This command can not be used by ingame players.";
public static final String COMMAND_TO_FEW_ARGS = "<b>Too few arguments. <i>Use like this:";
public static final String COMMAND_TO_MANY_ARGS = "<b>Strange arguments %s<b>.";
public static final String COMMAND_TO_MANY_ARGS2 = "<i>Use the command like this:";
public static final String COMMAND_TOO_FEW_ARGS = "<b>Not enough command input. <i>You should use it like this:";
public static final String COMMAND_TOO_MANY_ARGS = "<b>Too much command input %s<b>.";
public static final String COMMAND_TOO_MANY_ARGS2 = "<i>You should use the command like this:";
public static final String COMMAND_NO_SUCH_SUB = "<b>The server couldn't find the command \"<c>%s<b>\".";
public static final String COMMAND_SUGGEST_SUB = "<i>Maybe you could try %s";
public static final String COMMAND_GET_HELP = "<i>Use %s <i>to see commands.";
}

View File

@ -10,6 +10,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
@ -102,6 +103,7 @@ public class MassiveCommand
protected List<MassiveCommand> subCommands;
public List<MassiveCommand> getSubCommands() { return this.subCommands; }
public void setSubCommands(List<MassiveCommand> subCommands) { this.subCommands = subCommands; }
public boolean isParentCommand() { return this.getSubCommands().size() > 0; }
public MassiveCommand getSubCommand(String alias)
{
@ -437,8 +439,8 @@ public class MassiveCommand
{
if (sender != null)
{
msg(Lang.COMMAND_TO_FEW_ARGS);
sender.sendMessage(this.getUseageTemplate());
msg(Lang.COMMAND_TOO_FEW_ARGS);
sendMessage(this.getUseageTemplate());
}
return false;
}
@ -447,11 +449,33 @@ public class MassiveCommand
{
if (sender != null)
{
// Get the to many string slice
List<String> theToMany = args.subList(this.getRequiredArgs().size() + this.optionalArgs.size(), args.size());
msg(Lang.COMMAND_TO_MANY_ARGS, Txt.implodeCommaAndDot(theToMany, Txt.parse("<aqua>%s"), Txt.parse("<b>, "), Txt.parse("<b> and "), ""));
msg(Lang.COMMAND_TO_MANY_ARGS2);
sender.sendMessage(this.getUseageTemplate());
if (this.isParentCommand())
{
String arg = this.arg(0);
// Try Levenshtein
List<String> matches = this.getSimilarSubcommandAliases(arg, this.getMaxLevenshteinDistanceForArg(arg));
msg(Lang.COMMAND_NO_SUCH_SUB, this.getUseageTemplate() + " " + arg);
if ( ! matches.isEmpty())
{
String suggest = Txt.parse(Txt.implodeCommaAnd(matches, "<i>, <c>", " <i>or <c>"));
msg(Lang.COMMAND_SUGGEST_SUB, this.getUseageTemplate() + " " + suggest);
}
else
{
msg(Lang.COMMAND_GET_HELP, this.getUseageTemplate());
}
}
else
{
// Get the too many string slice
List<String> theTooMany = args.subList(this.getRequiredArgs().size() + this.optionalArgs.size(), args.size());
msg(Lang.COMMAND_TOO_MANY_ARGS, Txt.implodeCommaAndDot(theTooMany, Txt.parse("<aqua>%s"), Txt.parse("<b>, "), Txt.parse("<b> and "), ""));
msg(Lang.COMMAND_TOO_MANY_ARGS2);
sendMessage(this.getUseageTemplate());
}
}
return false;
}
@ -462,6 +486,47 @@ public class MassiveCommand
return this.isArgsValid(args, null);
}
// -------------------------------------------- //
// MATCHING SUGGESTIONS
// -------------------------------------------- //
public List<String> getSimilarAliases(String arg, int maxLevenshteinDistance)
{
arg = arg.toLowerCase();
List<String> matches = new ArrayList<String>();
for (String alias : this.getAliases())
{
String aliaslc = alias.toLowerCase();
int distance = StringUtils.getLevenshteinDistance(arg, aliaslc);
if (distance > maxLevenshteinDistance) continue;
matches.add(alias);
}
return matches;
}
public List<String> getSimilarSubcommandAliases(String arg, int maxLevenshteinDistance)
{
// Try Levenshtein
List<String> matches = new ArrayList<String>();
for (MassiveCommand sub : this.getSubCommands())
{
matches.addAll(sub.getSimilarAliases(arg, maxLevenshteinDistance));
}
return matches;
}
public int getMaxLevenshteinDistanceForArg(String arg)
{
if (arg.length() <= 1) return 0; // When dealing with 1 character aliases, there is way too many options. So we don't suggest.
if (arg.length() <= 3) return 1; // When dealing with low length aliases, there too many options. So we won't suggest much
if (arg.length() < 8) return 2; // 2 is default.
return 3; // If it were 8 characters or more, we end up here. Because many characters allow for more typos.
}
// -------------------------------------------- //
// HELP AND USAGE INFORMATION
// -------------------------------------------- //

View File

@ -1,7 +1,10 @@
package com.massivecraft.massivecore.cmd.arg;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender;
import com.massivecraft.massivecore.MassiveException;
@ -33,18 +36,28 @@ public abstract class ARAbstractSelect<T> extends ArgReaderAbstract<T>
{
T result = this.select(arg, sender);
if (result == null)
{
if (result != null) return result;
MassiveException exception = new MassiveException();
exception.addMsg("<b>No %s matches \"<h>%s<b>\".", this.typename(), arg);
if (this.canList(sender))
{
Collection<String> names = this.altNames(sender);
if (names.size() == 0)
// Try Levenshtein
List<String> matches = this.getMatchingAltNames(arg, sender, this.getMaxLevenshteinDistanceForArg(arg));
if (names.isEmpty())
{
exception.addMsg("<i>Note: There is no %s available.", this.typename());
}
else if ( ! matches.isEmpty() && matches.size() < LIST_COUNT_MAX)
{
// For some reason the arguments doesn't get parsed.
String suggest = Txt.parse(Txt.implodeCommaAnd(matches, "<i>, <h>", " <i>or <h>"));
exception.addMsg("<i>Did you mean <h>%s<i>?", suggest);
}
else if (names.size() > LIST_COUNT_MAX)
{
exception.addMsg("<i>More than %d alternatives available.", LIST_COUNT_MAX);
@ -62,7 +75,29 @@ public abstract class ARAbstractSelect<T> extends ArgReaderAbstract<T>
throw exception;
}
return result;
public List<String> getMatchingAltNames(String arg, CommandSender sender, int maxLevenshteinDistance)
{
arg = arg.toLowerCase();
// Try Levenshtein
List<String> matches = new ArrayList<String>();
for (String alias : this.altNames(sender))
{
String aliaslc = alias.toLowerCase();
int distance = StringUtils.getLevenshteinDistance(arg, aliaslc);
if (distance > maxLevenshteinDistance) continue;
matches.add(alias);
}
return matches;
}
public int getMaxLevenshteinDistanceForArg(String arg)
{
if (arg.length() <= 1) return 0; // When dealing with 1 character aliases, there is way too many options.
if (arg.length() < 8) return 1; // 1 is default.
return 2; // If it were 8 characters or more, we end up here. Because many characters allow for more typos.
}
}