So I decided to made a small tabApi to practice api's... It would be great if someone can give me some hints to make it even better. API: Code:java import java.util.ArrayList;import java.util.Collections;import java.util.List; import org.bukkit.Bukkit;import org.bukkit.Material;import org.bukkit.entity.Player;import org.bukkit.util.StringUtil; import com.google.common.collect.ImmutableList; public class TabApi { private List<String> values = new ArrayList<String>(); private static List<String> materials;static {ArrayList<String> materialList = new ArrayList<String>();for (Material material : Material.values()) {materialList.add(material.name());}Collections.sort(materialList);materials = ImmutableList.copyOf(materialList);} public void add(String line) {line = line.replaceAll("[ ]+", " "); if (!values.contains(line)) {values.add(line);}} public List<String> getPossibleCommandCompletions(String command, String[] args) {for (String a : args) {command += " " + a;}command = "/" + command;return getPossibleCompletions(command);}public List<String> getPossibleCompletions(String command) { List<String> list = new ArrayList<String>();List<String> possibleList = new ArrayList<String>(); int l = command.lastIndexOf(' '); String start;if (l == -1) {start = command;} else {start = command.substring(0, l);} for(String s : values) {if (this.startsWith(s, start)) {possibleList.add(s);try {String suggestion = s.substring(l + 1);if (suggestion.contains(" ")) {suggestion = suggestion.substring(0, suggestion.indexOf(' '));} if (suggestion.equalsIgnoreCase("{player}")) {for (Player player : Bukkit.getOnlinePlayers()) {list.add(player.getName());}} else if(suggestion.equalsIgnoreCase("{block}")) {list.addAll(getBlock(command.substring(l + 1)));} else if (suggestion.equalsIgnoreCase("{int}") || suggestion.equalsIgnoreCase("{string}")) { } else {if (!list.contains(suggestion)) {list.add(suggestion);}}} catch (Exception e) { }}}return StringUtil.copyPartialMatches(command.substring(l + 1), list, new ArrayList<String>());} /*** @param suggestion* @param s2* @return if (suggestion.startswith(s2)) return true*/private boolean startsWith(String suggestion, String s2) {if (!suggestion.contains("{player}") && !suggestion.contains("{block}") && !suggestion.contains("{int}") && !suggestion.contains("{string}")) return suggestion.startsWith(s2);String[] suggestionArray = suggestion.split(" ");String[] args2 = s2.split(" "); //if args2 has a larger index than suggestionArrayif (suggestionArray.length < args2.length) return false;//suggestionArray has more or equals the index of args2 for (int i = 0; i < args2.length; i++) {if (suggestionArray[I].equalsIgnoreCase("{player}") || suggestionArray[I].equalsIgnoreCase("{block}") || suggestionArray[I].equalsIgnoreCase("{int}") || suggestionArray[I].equalsIgnoreCase("{string}")) {[/I][/I][/I][/I][I] continue;[/I][I] } else {[/I][I] if (!suggestionArray[I].equalsIgnoreCase(args2[I])) return false;[/I][/I][/I][I] }[/I][I] }[/I][I] return true;[/I][I] }[/I] [I] private List<String> getBlock(String substring) {[/I][I] final String arg = substring;[/I][I] final List<String> materials = TabApi.materials;[/I][I] List<String> completion = null;[/I] [I] final int size = materials.size();[/I][I] int i = Collections.binarySearch(materials, arg, String.CASE_INSENSITIVE_ORDER);[/I] [I] if (i < 0) {[/I][I] // Insertion (start) index[/I][I] i = -1 - i;[/I][I] }[/I] [I] for ( ; i < size; i++) {[/I][I] String material = materials.get(i);[/I][I] if (StringUtil.startsWithIgnoreCase(material, arg)) {[/I][I] if (completion == null) {[/I][I] completion = new ArrayList<String>();[/I][I] }[/I][I] completion.add(material);[/I][I] } else {[/I][I] break;[/I][I] }[/I][I] }[/I] [I] if (completion != null) {[/I][I] return completion;[/I][I] }[/I][I] return ImmutableList.of();[/I][I] }[/I][I]}[/I][I][/I] How to use: Code:java @Overridepublic List<String> onTabComplete(CommandSender sender, Command cmd, String alias, String[] args) {//If the sender is not a player we wouldn't need to do these tab stuff.if (!(sender instanceof Player)) {return new ArrayList<String>();} //Create a new instance of the tabApiTabApi tabApi = new TabApi();//Get the player object from sender.Player player = (Player) sender; //Start registering the complete commands.//example: tabApi.add("/catchat channel create {string} {string}");//Specials://{string} means that the player should input a string//{int} same but for an integer (or any number actually)//{block} Used for when the player has to enter a block (also has tab completion)String command = cmd.getName();tabApi.add("/" + command + " channel");tabApi.add("/" + command + " channel help"); //If the player does not have these permissions then the tab completion won't work for these.if (CatChat.hasPerms(player, "catchat.channel.create")) {tabApi.add("/" + command + " channel create {string} {string}");} if (CatChat.hasPerms(player, "catchat.channel.join")) {tabApi.add("/" + command + " channel join {string} {string}");tabApi.add("/" + command + " channel leave");} if (CatChat.hasPerms(player, "catchat.channel.ban")) {tabApi.add("/" + command + " channel ban {string}");tabApi.add("/" + command + " channel unban {string}");} if(CatChat.hasPerms(player, "catchat.admin")) {tabApi.add("/" + command + " admin reload");tabApi.add("/" + command + " admin prefix {string} {string}");tabApi.add("/" + command + " admin suffix {string} {string}");tabApi.add("/" + command + " admin priority {string} {int}");} //Finally, returns the list of possible completions, args are the arguments passed into the command, and command is the command. (so without the '/')return tabApi.getPossibleCommandCompletions(command, args);}