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_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_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_TOO_FEW_ARGS = "<b>Not enough command input. <i>You should use it like this:";
public static final String COMMAND_TO_MANY_ARGS = "<b>Strange arguments %s<b>."; public static final String COMMAND_TOO_MANY_ARGS = "<b>Too much command input %s<b>.";
public static final String COMMAND_TO_MANY_ARGS2 = "<i>Use the command like this:"; 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.Map.Entry;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
@ -102,6 +103,7 @@ public class MassiveCommand
protected List<MassiveCommand> subCommands; protected List<MassiveCommand> subCommands;
public List<MassiveCommand> getSubCommands() { return this.subCommands; } public List<MassiveCommand> getSubCommands() { return this.subCommands; }
public void setSubCommands(List<MassiveCommand> subCommands) { this.subCommands = subCommands; } public void setSubCommands(List<MassiveCommand> subCommands) { this.subCommands = subCommands; }
public boolean isParentCommand() { return this.getSubCommands().size() > 0; }
public MassiveCommand getSubCommand(String alias) public MassiveCommand getSubCommand(String alias)
{ {
@ -437,8 +439,8 @@ public class MassiveCommand
{ {
if (sender != null) if (sender != null)
{ {
msg(Lang.COMMAND_TO_FEW_ARGS); msg(Lang.COMMAND_TOO_FEW_ARGS);
sender.sendMessage(this.getUseageTemplate()); sendMessage(this.getUseageTemplate());
} }
return false; return false;
} }
@ -447,11 +449,33 @@ public class MassiveCommand
{ {
if (sender != null) if (sender != null)
{ {
// Get the to many string slice if (this.isParentCommand())
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 "), "")); String arg = this.arg(0);
msg(Lang.COMMAND_TO_MANY_ARGS2);
sender.sendMessage(this.getUseageTemplate()); // 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; return false;
} }
@ -462,6 +486,47 @@ public class MassiveCommand
return this.isArgsValid(args, null); 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 // HELP AND USAGE INFORMATION
// -------------------------------------------- // // -------------------------------------------- //

View File

@ -1,7 +1,10 @@
package com.massivecraft.massivecore.cmd.arg; package com.massivecraft.massivecore.cmd.arg;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import com.massivecraft.massivecore.MassiveException; import com.massivecraft.massivecore.MassiveException;
@ -33,36 +36,68 @@ public abstract class ARAbstractSelect<T> extends ArgReaderAbstract<T>
{ {
T result = this.select(arg, sender); 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)
{
exception.addMsg("<i>Note: There is no %s available.", this.typename());
}
else if (names.size() > LIST_COUNT_MAX)
{
exception.addMsg("<i>More than %d alternatives available.", LIST_COUNT_MAX);
}
else
{
String format = Txt.parse("<h>%s");
String comma = Txt.parse("<i>, ");
String and = Txt.parse(" <i>or ");
String dot = Txt.parse("<i>.");
exception.addMsg("<i>Use %s", Txt.implodeCommaAndDot(names, format, comma, and, dot));
}
}
throw exception;
}
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);
// 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);
}
else
{
String format = Txt.parse("<h>%s");
String comma = Txt.parse("<i>, ");
String and = Txt.parse(" <i>or ");
String dot = Txt.parse("<i>.");
exception.addMsg("<i>Use %s", Txt.implodeCommaAndDot(names, format, comma, and, dot));
}
}
throw exception;
}
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.
} }
} }