Implement tab completion for permissions

This commit is contained in:
Luck
2016-11-24 19:07:01 +00:00
Unverified
parent 69b3c96e09
commit cf456cff81
15 changed files with 278 additions and 40 deletions
@@ -44,6 +44,7 @@ import me.lucko.luckperms.common.users.UserManager;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.DebugHandler;
import me.lucko.luckperms.common.utils.LocaleManager;
import me.lucko.luckperms.common.utils.PermissionCache;
import java.io.File;
import java.util.*;
@@ -147,6 +148,12 @@ public interface LuckPermsPlugin {
*/
DebugHandler getDebugHandler();
/**
* Gets the permission caching instance for the platform.
* @return the permission cache instance
*/
PermissionCache getPermissionCache();
/**
* Execute a runnable asynchronously
* @param r the task to run
@@ -56,7 +56,8 @@ public class PermissionCalculator {
public Tristate getPermissionValue(String permission) {
permission = permission.toLowerCase();
Tristate t = cache.getUnchecked(permission);
plugin.getDebugHandler().printOutput(objectName, permission, t);
plugin.getDebugHandler().offer(objectName, permission, t);
plugin.getPermissionCache().offer(permission);
return t;
}
@@ -22,6 +22,7 @@
package me.lucko.luckperms.common.commands;
import com.google.common.base.Splitter;
import lombok.Getter;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.commands.sender.Sender;
@@ -31,11 +32,9 @@ import me.lucko.luckperms.common.constants.Permission;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.utils.PermissionCache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -101,6 +100,55 @@ public abstract class SubCommand<T> extends Command<T, Void> {
}
}
public static List<String> getPermissionTabComplete(List<String> args, PermissionCache cache) {
if (args.size() <= 1) {
if (args.isEmpty() || args.get(0).equals("")) {
return cache.getRootNode().getChildren()
.map(Map::keySet)
.map(s -> s.stream().collect(Collectors.toList()))
.orElse(Collections.emptyList());
}
String start = args.get(0).toLowerCase();
List<String> parts = new ArrayList<>(Splitter.on('.').splitToList(start));
PermissionCache.Node root = cache.getRootNode();
if (parts.size() <= 1) {
if (!root.getChildren().isPresent()) {
return Collections.emptyList();
}
return root.getChildren().get().keySet().stream().filter(s -> s.startsWith(start)).collect(Collectors.toList());
}
String incomplete = parts.remove(parts.size() - 1);
for (String s : parts) {
if (!root.getChildren().isPresent()) {
return Collections.emptyList();
}
PermissionCache.Node n = root.getChildren().get().get(s);
if (n == null) {
return Collections.emptyList();
}
root = n;
}
if (!root.getChildren().isPresent()) {
return Collections.emptyList();
}
return root.getChildren().get().keySet().stream()
.filter(s -> s.startsWith(incomplete))
.map(s -> parts.stream().collect(Collectors.joining(".")) + "." + s)
.collect(Collectors.toList());
}
return Collections.emptyList();
}
private static List<String> getTabComplete(List<String> options, List<String> args) {
if (args.size() <= 1) {
if (args.isEmpty() || args.get(0).equalsIgnoreCase("")) {
@@ -38,6 +38,8 @@ import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import static me.lucko.luckperms.common.commands.SubCommand.getPermissionTabComplete;
public class PermissionCheck extends SharedSubCommand {
public PermissionCheck() {
super("check", "Checks to see if the object has a certain permission node", Permission.USER_PERM_CHECK,
@@ -70,4 +72,9 @@ public class PermissionCheck extends SharedSubCommand {
return CommandResult.SUCCESS;
}
@Override
public List<String> onTabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return getPermissionTabComplete(args, plugin.getPermissionCache());
}
}
@@ -39,6 +39,8 @@ import me.lucko.luckperms.common.utils.Predicates;
import java.util.List;
import static me.lucko.luckperms.common.commands.SubCommand.getPermissionTabComplete;
public class PermissionCheckInherits extends SharedSubCommand {
public PermissionCheckInherits() {
super("checkinherits", "Checks to see if the object inherits a certain permission node",
@@ -79,4 +81,9 @@ public class PermissionCheckInherits extends SharedSubCommand {
(location != null ? " &7(inherited from &a" + location + "&7)" : ""));
return CommandResult.SUCCESS;
}
@Override
public List<String> onTabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return getPermissionTabComplete(args, plugin.getPermissionCache());
}
}
@@ -41,6 +41,7 @@ import java.util.List;
import java.util.stream.Collectors;
import static me.lucko.luckperms.common.commands.SubCommand.getBoolTabComplete;
import static me.lucko.luckperms.common.commands.SubCommand.getPermissionTabComplete;
public class PermissionSet extends SharedSubCommand {
public PermissionSet() {
@@ -94,6 +95,10 @@ public class PermissionSet extends SharedSubCommand {
@Override
public List<String> onTabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return getBoolTabComplete(args);
List<String> ret = getBoolTabComplete(args);
if (!ret.isEmpty()) {
return ret;
}
return getPermissionTabComplete(args, plugin.getPermissionCache());
}
}
@@ -42,6 +42,7 @@ import java.util.List;
import java.util.stream.Collectors;
import static me.lucko.luckperms.common.commands.SubCommand.getBoolTabComplete;
import static me.lucko.luckperms.common.commands.SubCommand.getPermissionTabComplete;
public class PermissionSetTemp extends SharedSubCommand {
public PermissionSetTemp() {
@@ -104,6 +105,10 @@ public class PermissionSetTemp extends SharedSubCommand {
@Override
public List<String> onTabComplete(LuckPermsPlugin plugin, Sender sender, List<String> args) {
return getBoolTabComplete(args);
List<String> ret = getBoolTabComplete(args);
if (!ret.isEmpty()) {
return ret;
}
return getPermissionTabComplete(args, plugin.getPermissionCache());
}
}
@@ -32,13 +32,36 @@ import me.lucko.luckperms.common.constants.Message;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
public class DebugHandler {
private final Map<Receiver, List<String>> listeners = new ConcurrentHashMap<>();
private final Map<Receiver, List<String>> listeners;
private final Queue<Data> queue;
public void printOutput(String checked, String node, Tristate value) {
public DebugHandler(Executor executor) {
listeners = new ConcurrentHashMap<>();
queue = new ConcurrentLinkedQueue<>();
executor.execute(() -> {
while (true) {
for (Data e; (e = queue.poll()) != null;) {
handleOutput(e.getChecked(), e.getNode(), e.getValue());
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
private void handleOutput(String checked, String node, Tristate value) {
all:
for (Map.Entry<Receiver, List<String>> e : listeners.entrySet()) {
for (String filter : e.getValue()) {
@@ -57,6 +80,10 @@ public class DebugHandler {
}
}
public void offer(String checked, String node, Tristate value) {
queue.offer(new Data(checked, node, value));
}
public void register(Sender sender, List<String> filters) {
listeners.put(new Receiver(sender.getUuid(), sender), ImmutableList.copyOf(filters));
}
@@ -72,4 +99,12 @@ public class DebugHandler {
private final UUID uuid;
private final Sender sender;
}
@Getter
@AllArgsConstructor
private static final class Data {
private final String checked;
private final String node;
private final Tristate value;
}
}
@@ -0,0 +1,91 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package me.lucko.luckperms.common.utils;
import com.google.common.base.Splitter;
import lombok.Getter;
import lombok.NonNull;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
public class PermissionCache {
@Getter
private final Node rootNode;
private final Queue<String> queue;
public PermissionCache(Executor executor) {
rootNode = new Node();
queue = new ConcurrentLinkedQueue<>();
executor.execute(() -> {
while (true) {
for (String e; (e = queue.poll()) != null;) {
insert(e.toLowerCase());
}
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
public void offer(@NonNull String permission) {
queue.offer(permission);
}
private void insert(String permission) {
List<String> parts = Splitter.on('.').splitToList(permission);
Node current = rootNode;
for (String part : parts) {
current = current.getChildMap().computeIfAbsent(part, s -> new Node());
}
}
public static class Node {
private Map<String, Node> children = null;
// lazy init
private synchronized Map<String, Node> getChildMap() {
if (children == null) {
children = new ConcurrentHashMap<>();
}
return children;
}
public Optional<Map<String, Node>> getChildren() {
return Optional.ofNullable(children);
}
}
}