Adds more tab-completion help methods
All checks were successful
KnarCraft/KnarLib/pipeline/head This commit looks good
All checks were successful
KnarCraft/KnarLib/pipeline/head This commit looks good
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
package net.knarcraft.knarlib.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
@@ -13,6 +19,9 @@ import java.util.function.BiFunction;
|
||||
@SuppressWarnings("unused")
|
||||
public final class TabCompletionHelper {
|
||||
|
||||
private static List<String> plugins;
|
||||
private static Map<String, List<String>> permissions;
|
||||
|
||||
private TabCompletionHelper() {
|
||||
}
|
||||
|
||||
@@ -27,46 +36,41 @@ public final class TabCompletionHelper {
|
||||
* @param filterer <p>The filterer (filterMatching) to use to filter by typed text</p>
|
||||
* @return <p>Available tab-completions</p>
|
||||
*/
|
||||
public static @NotNull List<String> getStringList(@NotNull List<String> values, @NotNull String typedText,
|
||||
@NotNull
|
||||
public static List<String> getStringList(@NotNull List<String> values, @NotNull String typedText,
|
||||
@Nullable BiFunction<List<String>, String, List<String>> filterer) {
|
||||
// If the typed text is blank, only filter, if necessary
|
||||
if (typedText.isBlank() || !typedText.contains(",")) {
|
||||
if (filterer != null) {
|
||||
return filterer.apply(values, typedText);
|
||||
} else {
|
||||
return values;
|
||||
}
|
||||
} else {
|
||||
// The argument base is everything the player has typed, until the last comma
|
||||
List<String> arguments = List.of(typedText.split(","));
|
||||
String base;
|
||||
List<String> baseArguments;
|
||||
if (typedText.endsWith(",")) {
|
||||
baseArguments = arguments;
|
||||
} else {
|
||||
baseArguments = arguments.subList(0, arguments.size() - 1);
|
||||
}
|
||||
base = String.join(",", baseArguments) + ",";
|
||||
String lastArgument = arguments.get(arguments.size() - 1);
|
||||
|
||||
List<String> items = List.of(typedText.split(","));
|
||||
|
||||
// Filter available values by the text after the last comma only
|
||||
List<String> filtered;
|
||||
if (filterer != null && !typedText.endsWith(",")) {
|
||||
filtered = filterer.apply(new ArrayList<>(values), lastArgument);
|
||||
filtered = filterer.apply(new ArrayList<>(values), items.get(items.size() - 1));
|
||||
} else {
|
||||
filtered = values;
|
||||
}
|
||||
|
||||
// Remove any already used values
|
||||
// Add the prefix to all filtered values
|
||||
List<String> baseItems = typedText.endsWith(",") ? items : items.subList(0, items.size() - 1);
|
||||
String base = String.join(",", baseItems) + ",";
|
||||
List<String> unused = new ArrayList<>();
|
||||
for (String string : filtered) {
|
||||
if (!baseArguments.contains(string)) {
|
||||
// Remove any already used values
|
||||
if (!baseItems.contains(string)) {
|
||||
unused.add(base + string);
|
||||
}
|
||||
}
|
||||
|
||||
return unused;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds tab complete values that contain the typed text
|
||||
@@ -75,7 +79,8 @@ public final class TabCompletionHelper {
|
||||
* @param typedText <p>The text the player has started typing</p>
|
||||
* @return <p>The given string values that contain the player's typed text</p>
|
||||
*/
|
||||
public static @NotNull List<String> filterMatchingContains(@NotNull List<String> values, @NotNull String typedText) {
|
||||
@NotNull
|
||||
public static List<String> filterMatchingContains(@NotNull List<String> values, @NotNull String typedText) {
|
||||
List<String> configValues = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
if (value.toLowerCase().contains(typedText.toLowerCase())) {
|
||||
@@ -92,7 +97,8 @@ public final class TabCompletionHelper {
|
||||
* @param typedText <p>The text the player has started typing</p>
|
||||
* @return <p>The given string values that start with the player's typed text</p>
|
||||
*/
|
||||
public static @NotNull List<String> filterMatchingStartsWith(@NotNull List<String> values, @NotNull String typedText) {
|
||||
@NotNull
|
||||
public static List<String> filterMatchingStartsWith(@NotNull List<String> values, @NotNull String typedText) {
|
||||
List<String> configValues = new ArrayList<>();
|
||||
for (String value : values) {
|
||||
if (value.toLowerCase().startsWith(typedText.toLowerCase())) {
|
||||
@@ -103,14 +109,181 @@ public final class TabCompletionHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the "base" string from the given arguments, not including the incomplete last argument
|
||||
* Gets a list of enum names from the given enum type
|
||||
*
|
||||
* @param arguments <p>The string arguments to get the base of</p>
|
||||
* @return <p>The base string</p>
|
||||
* @param enumType <p>The type of enum to list</p>
|
||||
* @param <E> <p>The type of enum to list values for</p>
|
||||
* @return <p>The names of the enums in the type</p>
|
||||
*/
|
||||
private static @NotNull String getBase(@NotNull List<String> arguments) {
|
||||
List<String> baseArray = arguments.subList(0, arguments.size() - 1);
|
||||
return String.join(",", baseArray) + ",";
|
||||
@NotNull
|
||||
public static <E extends Enum<E>> List<String> getEnumList(Class<E> enumType) {
|
||||
return Arrays.stream(enumType.getEnumConstants()).map((item) -> item.name().toLowerCase()).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets tab-completions with only remaining text, from a list of full strings
|
||||
*
|
||||
* <p>This is specifically useful when tab-completing for values that contain spaces. This allows tab-completions to
|
||||
* only list remaining text missing for tab-completing each value, instead of listing the entire strings.</p>
|
||||
*
|
||||
* @param arguments <p>The arguments given by the user</p>
|
||||
* @param filtered <p>Tab-completions filtered by user input</p>
|
||||
* @param offset <p>The offset to use, if other arguments are preceding the relevant arguments</p>
|
||||
* @param prefix <p>The prefix to use before each tab-completion</p>
|
||||
* @return <p>The cleaned tab-completions</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static List<String> getCleanedTabCompletions(@NotNull String[] arguments, @NotNull List<String> filtered,
|
||||
int offset, @NotNull String prefix) {
|
||||
List<String> cleaned = new ArrayList<>();
|
||||
int offsetCount = arguments.length - offset;
|
||||
for (String name : filtered) {
|
||||
int startIndex = arguments.length == 0 ? 0 : (offsetCount > 1 ? offsetCount - 1 : 0);
|
||||
String[] parts = name.split(" ", -1);
|
||||
// Skip non-matching entries
|
||||
if ((offsetCount > 1 && !parts[offsetCount - 2].equalsIgnoreCase(arguments[offsetCount - 2 + offset])) ||
|
||||
startIndex >= parts.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startIndex == 0) {
|
||||
cleaned.add(prefix + name);
|
||||
} else {
|
||||
// Skip the required amount of items
|
||||
StringBuilder builder = new StringBuilder(prefix).append(parts[startIndex]);
|
||||
for (int i = startIndex + 1; i < parts.length; i++) {
|
||||
builder.append(" ").append(parts[i]);
|
||||
}
|
||||
cleaned.add(builder.toString());
|
||||
}
|
||||
}
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all arguments to a string with spaces
|
||||
*
|
||||
* @param arguments <p>The arguments to merge</p>
|
||||
* @param stripLastN <p>How many of the last arguments to ignore</p>
|
||||
* @return <p>The merged arguments</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static String mergeArguments(@NotNull String[] arguments, int stripLastN) {
|
||||
return mergeArguments(0, arguments, stripLastN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all arguments to a string with spaces
|
||||
*
|
||||
* @param stripFirstN <p>How many of the first arguments to ignore</p>
|
||||
* @param arguments <p>The arguments to merge</p>
|
||||
* @return <p>The merged arguments</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static String mergeArguments(int stripFirstN, @NotNull String[] arguments) {
|
||||
return mergeArguments(stripFirstN, arguments, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges all arguments to a string with spaces
|
||||
*
|
||||
* @param stripFirstN <p>How many of the first arguments to ignore</p>
|
||||
* @param arguments <p>The arguments to merge</p>
|
||||
* @param stripLastN <p>How many of the last arguments to ignore</p>
|
||||
* @return <p>The merged arguments</p>
|
||||
*/
|
||||
@NotNull
|
||||
public static String mergeArguments(int stripFirstN, @NotNull String[] arguments, int stripLastN) {
|
||||
StringBuilder builder = new StringBuilder(arguments[stripFirstN]);
|
||||
for (int i = stripFirstN + 1; i < arguments.length - stripLastN; i++) {
|
||||
builder.append(" ").append(arguments[i]);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tab complete value for the permission typed
|
||||
*
|
||||
* @param typedNode <p>The full permission node typed by the player</p>
|
||||
* @return <p>All known valid auto-complete options</p>
|
||||
*/
|
||||
@NotNull
|
||||
private static List<String> tabCompletePermission(@NotNull String typedNode) {
|
||||
if (plugins == null) {
|
||||
loadAvailablePermissions();
|
||||
}
|
||||
|
||||
String permissionPrefix = parsePermissionPrefix(typedNode);
|
||||
typedNode = typedNode.replaceFirst(permissionPrefix, typedNode);
|
||||
if (!typedNode.contains(".")) {
|
||||
return TabCompletionHelper.filterMatchingStartsWith(plugins, typedNode);
|
||||
}
|
||||
|
||||
List<String> matchingPermissions = permissions.get(typedNode.substring(0, typedNode.lastIndexOf(".")));
|
||||
if (matchingPermissions != null) {
|
||||
List<String> output = TabCompletionHelper.filterMatchingStartsWith(matchingPermissions, typedNode);
|
||||
return output.stream().map((item) -> permissionPrefix + item).toList();
|
||||
} else {
|
||||
if (!permissionPrefix.isEmpty()) {
|
||||
return List.of(permissionPrefix);
|
||||
} else {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses comma-separated preceding permission values as a prefix
|
||||
*
|
||||
* @param typedNode <p>The permission node string the user typed</p>
|
||||
* @return <p>The prefix</p>
|
||||
*/
|
||||
@NotNull
|
||||
private static String parsePermissionPrefix(@NotNull String typedNode) {
|
||||
if (!typedNode.contains(",")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder permissionListString = new StringBuilder();
|
||||
String[] permissionList = typedNode.split(",");
|
||||
for (int i = 0; i < permissionList.length - 1; i++) {
|
||||
permissionListString.append(permissionList[i]).append(",");
|
||||
}
|
||||
return permissionListString.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all permissions available from bukkit plugins
|
||||
*/
|
||||
private static void loadAvailablePermissions() {
|
||||
plugins = new ArrayList<>();
|
||||
permissions = new HashMap<>();
|
||||
|
||||
for (Permission permission : Bukkit.getPluginManager().getPermissions()) {
|
||||
loadPermission(permission.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a given permission into the proper lists and maps
|
||||
*
|
||||
* @param permissionName <p>The permission to load</p>
|
||||
*/
|
||||
private static void loadPermission(@NotNull String permissionName) {
|
||||
String[] permissionParts = permissionName.split("\\.");
|
||||
if (permissionParts.length == 1) {
|
||||
plugins.add(permissionParts[0]);
|
||||
} else if (permissionParts.length > 1) {
|
||||
StringJoiner pathJoiner = new StringJoiner(".");
|
||||
for (int j = 0; j < permissionParts.length - 1; j++) {
|
||||
pathJoiner.add(permissionParts[j]);
|
||||
}
|
||||
String path = pathJoiner.toString();
|
||||
List<String> permissionList = permissions.computeIfAbsent(path, k -> new ArrayList<>());
|
||||
permissionList.add(permissionName);
|
||||
|
||||
loadPermission(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user