All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.aikar.commands.CommandCompletions Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2016-2017 Daniel Ennis (Aikar) - MIT License
 *
 *  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 co.aikar.commands;

import co.aikar.commands.apachecommonslang.ApacheCommonsLangUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


@SuppressWarnings({"WeakerAccess", "UnusedReturnValue"})
public class CommandCompletions {
    private static final String DEFAULT_ENUM_ID = "@__defaultenum__";
    private final CommandManager manager;
    // TODO: use a CompletionProvider that can return a delegated Id or provide values such as enum support
    private Map completionMap = new HashMap<>();
    private Map defaultCompletions = new HashMap<>();

    public CommandCompletions(CommandManager manager) {
        this.manager = manager;
        registerStaticCompletion("empty", Collections.emptyList());
        registerStaticCompletion("nothing", Collections.emptyList());
        registerStaticCompletion("timeunits", Arrays.asList("minutes", "hours", "days", "weeks", "months", "years"));
        registerAsyncCompletion("range", (c) -> {
            String config = c.getConfig();
            if (config == null) {
                return Collections.emptyList();
            }
            final String[] ranges = ACFPatterns.DASH.split(config);
            int start;
            int end;
            if (ranges.length != 2) {
                start = 0;
                end = ACFUtil.parseInt(ranges[0], 0);
            } else {
                start = ACFUtil.parseInt(ranges[0], 0);
                end = ACFUtil.parseInt(ranges[1], 0);
            }
            return IntStream.rangeClosed(start, end).mapToObj(Integer::toString).collect(Collectors.toList());
        });
    }

    /**
     * Registr a completion handler to provide command completions based on the user input.
     *
     * @param id
     * @param handler
     * @return
     */
    public CommandCompletionHandler registerCompletion(String id, CommandCompletionHandler handler) {
        return this.completionMap.put(prepareCompletionId(id), handler);
    }

    /**
     * Registr a completion handler to provide command completions based on the user input.
     * This handler is declared to be safe to be executed asynchronously.
     * 

* Not all platforms support this, so if the platform does not support asynchronous execution, * your handler will be executed on the main thread. *

* Use this anytime your handler does not need to access state that is not considered thread safe. *

* Use context.isAsync() to determine if you are async or not. * * @param id * @param handler * @return */ public CommandCompletionHandler registerAsyncCompletion(String id, AsyncCommandCompletionHandler handler) { return this.completionMap.put(prepareCompletionId(id), handler); } /** * Register a static list of command completions that will never change. * Like @CommandCompletion, values are | (PIPE) separated. *

* Example: foo|bar|baz * * @param id * @param list * @return */ public CommandCompletionHandler registerStaticCompletion(String id, String list) { return registerStaticCompletion(id, ACFPatterns.PIPE.split(list)); } /** * Register a static list of command completions that will never change * * @param id * @param completions * @return */ public CommandCompletionHandler registerStaticCompletion(String id, String[] completions) { return registerStaticCompletion(id, Arrays.asList(completions)); } /** * Register a static list of command completions that will never change. The list is obtained from the supplier * immediately as part of this method call. * * @param id * @param supplier * @return */ public CommandCompletionHandler registerStaticCompletion(String id, Supplier> supplier) { return registerStaticCompletion(id, supplier.get()); } /** * Register a static list of command completions that will never change * * @param id * @param completions * @return */ public CommandCompletionHandler registerStaticCompletion(String id, Collection completions) { return registerAsyncCompletion(id, x -> completions); } /** * Registers a completion handler such as @players to default apply to all command parameters of the specified types *

* This enables automatic completion support for parameters without manually defining it for custom objects * * @param id * @param classes */ public void setDefaultCompletion(String id, Class... classes) { // get completion with specified id id = prepareCompletionId(id); CommandCompletionHandler completion = completionMap.get(id); if (completion == null) { // Throw something because no completion with specified id throw new IllegalStateException("Completion not registered for " + id); } for (Class clazz : classes) { defaultCompletions.put(clazz, id); } } @NotNull private static String prepareCompletionId(String id) { return (id.startsWith("@") ? "" : "@") + id.toLowerCase(Locale.ENGLISH); } @NotNull List of(RegisteredCommand cmd, CommandIssuer sender, String[] args, boolean isAsync) { String[] completions = ACFPatterns.SPACE.split(cmd.complete); final int argIndex = args.length - 1; String input = args[argIndex]; String completion = argIndex < completions.length ? completions[argIndex] : null; if (completion == null || "*".equals(completion)) { completion = findDefaultCompletion(cmd, args); } if (completion == null && completions.length > 0) { String last = completions[completions.length - 1]; if (last.startsWith("repeat@")) { completion = last; } else if (argIndex >= completions.length && cmd.parameters[cmd.parameters.length - 1].consumesRest) { completion = last; } } if (completion == null) { return Collections.singletonList(input); } return getCompletionValues(cmd, sender, completion, args, isAsync); } String findDefaultCompletion(RegisteredCommand cmd, String[] args) { int i = 0; for (CommandParameter param : cmd.parameters) { if (param.canConsumeInput() && ++i == args.length) { Class type = param.getType(); while (type != null) { String completion = this.defaultCompletions.get(type); if (completion != null) { return completion; } type = type.getSuperclass(); } if (param.getType().isEnum()) { CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); //noinspection unchecked ctx.enumCompletionValues = ACFUtil.enumNames((Class>) param.getType()); return DEFAULT_ENUM_ID; } break; } } return null; } List getCompletionValues(RegisteredCommand command, CommandIssuer sender, String completion, String[] args, boolean isAsync) { if (DEFAULT_ENUM_ID.equals(completion)) { CommandOperationContext ctx = CommandManager.getCurrentCommandOperationContext(); return ctx.enumCompletionValues; } boolean repeat = completion.startsWith("repeat@"); if (repeat) { completion = completion.substring(6); } completion = manager.getCommandReplacements().replace(completion); List allCompletions = new ArrayList<>(); String input = args.length > 0 ? args[args.length - 1] : ""; for (String value : ACFPatterns.PIPE.split(completion)) { String[] complete = ACFPatterns.COLONEQUALS.split(value, 2); CommandCompletionHandler handler = this.completionMap.get(complete[0].toLowerCase(Locale.ENGLISH)); if (handler != null) { if (isAsync && !(handler instanceof AsyncCommandCompletionHandler)) { ACFUtil.sneaky(new SyncCompletionRequired()); return null; } String config = complete.length == 1 ? null : complete[1]; CommandCompletionContext context = manager.createCompletionContext(command, sender, input, config, args); try { //noinspection unchecked Collection completions = handler.getCompletions(context); //Handle completions with more than one word: if (!repeat && completions != null && command.parameters[command.parameters.length - 1].consumesRest && args.length > ACFPatterns.SPACE.split(command.complete).length) { String start = String.join(" ", args); completions = completions.stream() .map(s -> { if (s != null && s.split(" ").length >= args.length && ApacheCommonsLangUtil.startsWithIgnoreCase(s, start)) { String[] completionArgs = s.split(" "); return String.join(" ", Arrays.copyOfRange(completionArgs, args.length - 1, completionArgs.length)); } else { return s; } }).collect(Collectors.toList()); } if (completions != null) { allCompletions.addAll(completions); continue; } //noinspection ConstantIfStatement,ConstantConditions if (false) { // Hack to fool compiler. since its sneakily thrown. throw new CommandCompletionTextLookupException(); } } catch (CommandCompletionTextLookupException ignored) { // This should only happen if some other feedback error occured. } catch (Exception e) { command.handleException(sender, Arrays.asList(args), e); } // Something went wrong in lookup, fall back to input return Collections.singletonList(input); } else { // Plaintext value allCompletions.add(value); } } return allCompletions; } public interface CommandCompletionHandler { Collection getCompletions(C context) throws InvalidCommandArgument; } public interface AsyncCommandCompletionHandler extends CommandCompletionHandler { } public static class SyncCompletionRequired extends RuntimeException { } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy