Add tab-completion API. Fixes BUKKIT-2181. Adds BUKKIT-2602
CommandMap contains a method that will auto-complete commands appropriately. Before the first space, it searches for commands of which the sender has permission. After the first space, it delegates to the individual command. Vanilla commands contain implementations to mimic vanilla implementation. Exception would be give, that allows for name matching; a feature we already allowed as part of the command is now supported for auto-complete as well. Plugin commands can get a tab completer set to delegate the completion for. If no tab completer is set, it can check the executor to see if it implements the tab completion interface. It will also attempt to chain calls if null gets returned from these interfaces. Plugins also implement the new TabCompleter interface, to add ease-of-use for plugin developers, similar to the onCommand() method. The default command implementation simply searches for player names. To help facilitate command completion, a utility class was added with two functions. One checks two strings, to see if the specified string starts with (ignoring case) the second. The other method uses the first to selectively copy elements from one collection to another. By: Score_Under <seejay.11@gmail.com>
This commit is contained in:
@@ -1,13 +1,25 @@
|
||||
package org.bukkit.command;
|
||||
|
||||
import org.bukkit.command.defaults.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.bukkit.Server;
|
||||
import static org.bukkit.util.Java15Compat.Arrays_copyOfRange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.defaults.*;
|
||||
import org.bukkit.util.StringUtil;
|
||||
|
||||
public class SimpleCommandMap implements CommandMap {
|
||||
private static final Pattern PATTERN_ON_SPACE = Pattern.compile(" ", Pattern.LITERAL);
|
||||
protected final Map<String, Command> knownCommands = new HashMap<String, Command>();
|
||||
protected final Set<String> aliases = new HashSet<String>();
|
||||
private final Server server;
|
||||
@@ -81,7 +93,7 @@ public class SimpleCommandMap implements CommandMap {
|
||||
|
||||
Iterator<String> iterator = command.getAliases().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
if (!register((String) iterator.next(), fallbackPrefix, command, true)) {
|
||||
if (!register(iterator.next(), fallbackPrefix, command, true)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
@@ -150,7 +162,7 @@ public class SimpleCommandMap implements CommandMap {
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean dispatch(CommandSender sender, String commandLine) throws CommandException {
|
||||
String[] args = commandLine.split(" ");
|
||||
String[] args = PATTERN_ON_SPACE.split(commandLine);
|
||||
|
||||
if (args.length == 0) {
|
||||
return false;
|
||||
@@ -186,13 +198,82 @@ public class SimpleCommandMap implements CommandMap {
|
||||
}
|
||||
|
||||
public Command getCommand(String name) {
|
||||
Command target = knownCommands.get(name.toLowerCase());
|
||||
Command target = knownCommands.get(name.toLowerCase());
|
||||
if (target == null) {
|
||||
target = getFallback(name);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public List<String> tabComplete(CommandSender sender, String cmdLine) {
|
||||
Validate.notNull(sender, "Sender cannot be null");
|
||||
Validate.notNull(cmdLine, "Command line cannot null");
|
||||
|
||||
int spaceIndex = cmdLine.indexOf(' ');
|
||||
|
||||
if (spaceIndex == -1) {
|
||||
ArrayList<String> completions = new ArrayList<String>();
|
||||
Map<String, Command> knownCommands = this.knownCommands;
|
||||
|
||||
for (VanillaCommand command : fallbackCommands) {
|
||||
String name = command.getName();
|
||||
|
||||
if (!command.testPermissionSilent(sender)) {
|
||||
continue;
|
||||
}
|
||||
if (knownCommands.containsKey(name)) {
|
||||
// Don't let a vanilla command override a command added below
|
||||
// This has to do with the way aliases work
|
||||
continue;
|
||||
}
|
||||
if (!StringUtil.startsWithIgnoreCase(name, cmdLine)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
completions.add('/' + name);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Command> commandEntry : knownCommands.entrySet()) {
|
||||
Command command = commandEntry.getValue();
|
||||
|
||||
if (!command.testPermissionSilent(sender)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = commandEntry.getKey(); // Use the alias, not command name
|
||||
|
||||
if (StringUtil.startsWithIgnoreCase(name, cmdLine)) {
|
||||
completions.add('/' + name);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(completions, String.CASE_INSENSITIVE_ORDER);
|
||||
return completions;
|
||||
}
|
||||
|
||||
String commandName = cmdLine.substring(0, spaceIndex);
|
||||
Command target = getCommand(commandName);
|
||||
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!target.testPermissionSilent(sender)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String argLine = cmdLine.substring(spaceIndex + 1, cmdLine.length());
|
||||
String[] args = PATTERN_ON_SPACE.split(argLine, -1);
|
||||
|
||||
try {
|
||||
return target.tabComplete(sender, commandName, args);
|
||||
} catch (CommandException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
throw new CommandException("Unhandled exception executing tab-completer for '" + cmdLine + "' in " + target, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Command> getCommands() {
|
||||
return knownCommands.values();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user