From 24c2f015a465600586cf2ec6f47d80415fbcc3a9 Mon Sep 17 00:00:00 2001 From: WillBAnders <35618116+WillBAnders@users.noreply.github.com> Date: Sat, 25 Jul 2020 05:42:23 -0400 Subject: [PATCH] Add defined flags to CommandContext (#2033) * Allow context to track defined flags for complex flag parsing, such as with optional values. * Include flags in context snapshot, add flag to context after parsing, and delegate to valueElement for completion when present. * Inline MARK_TRUE_FUNC --- .../api/command/args/CommandContext.java | 50 ++++++++++++++++++- .../api/command/args/CommandFlags.java | 49 +++++++++++++++--- 2 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/spongepowered/api/command/args/CommandContext.java b/src/main/java/org/spongepowered/api/command/args/CommandContext.java index 77ea97079d0..2e38ce6e29b 100644 --- a/src/main/java/org/spongepowered/api/command/args/CommandContext.java +++ b/src/main/java/org/spongepowered/api/command/args/CommandContext.java @@ -29,6 +29,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; import org.spongepowered.api.command.CommandException; import org.spongepowered.api.command.CommandSource; import org.spongepowered.api.text.Text; @@ -38,6 +39,7 @@ import java.util.Collections; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.Set; /** * Context that a command is executed in. @@ -57,12 +59,14 @@ public final class CommandContext { public static final String TAB_COMPLETION = "tab-complete-50456"; // Random junk afterwards so we don't accidentally conflict with other args private final Multimap parsedArgs; + private final Set definedFlags; /** * Create a new empty CommandContext. */ public CommandContext() { this.parsedArgs = ArrayListMultimap.create(); + this.definedFlags = Sets.newHashSet(); } /** @@ -193,6 +197,24 @@ public void putArg(Text key, Object value) { putArg(ArgUtils.textToArgKey(key), value); } + /** + * Defines the flag as being present when parsing this context. + * + * @param key the key for the flag defined + */ + public void addFlag(String key) { + definedFlags.add(key); + } + + /** + * Defines the flag as being present when parsing this context. + * + * @param key the key for the flag defined + */ + public void addFlag(Text key) { + addFlag(ArgUtils.textToArgKey(key)); + } + /** * Perform a permissions check, throwing an exception if the required * permissions are not present. @@ -227,6 +249,26 @@ public boolean hasAny(Text key) { return hasAny(ArgUtils.textToArgKey(key)); } + /** + * Returns whether the given flag has been defined in this context. + * + * @param key The key to look up + * @return whether the flag is defined + */ + public boolean hasFlag(String key) { + return definedFlags.contains(key); + } + + /** + * Returns whether the given flag has been defined in this context. + * + * @param key The key to look up + * @return whether the flag is defined + */ + public boolean hasFlag(Text key) { + return hasFlag(ArgUtils.textToArgKey(key)); + } + /** * Gets a snapshot of the data inside this context to allow it to be * restored later. @@ -240,7 +282,7 @@ public boolean hasAny(Text key) { * {@link CommandContext} */ public Snapshot createSnapshot() { - return new Snapshot(this.parsedArgs); + return new Snapshot(this.parsedArgs, this.definedFlags); } /** @@ -252,6 +294,8 @@ public Snapshot createSnapshot() { public void applySnapshot(Snapshot snapshot) { this.parsedArgs.clear(); this.parsedArgs.putAll(snapshot.args); + this.definedFlags.clear(); + this.definedFlags.addAll(snapshot.flags); } /** @@ -261,9 +305,11 @@ public void applySnapshot(Snapshot snapshot) { public final class Snapshot { final Multimap args; + final Set flags; - Snapshot(Multimap args) { + Snapshot(Multimap args, Set flags) { this.args = ArrayListMultimap.create(args); + this.flags = Sets.newHashSet(flags); } } diff --git a/src/main/java/org/spongepowered/api/command/args/CommandFlags.java b/src/main/java/org/spongepowered/api/command/args/CommandFlags.java index d7434ed4aa6..3ca0e4e4bf3 100644 --- a/src/main/java/org/spongepowered/api/command/args/CommandFlags.java +++ b/src/main/java/org/spongepowered/api/command/args/CommandFlags.java @@ -24,7 +24,6 @@ */ package org.spongepowered.api.command.args; -import static org.spongepowered.api.command.args.GenericArguments.markTrue; import static org.spongepowered.api.command.args.GenericArguments.requiringPermission; import static org.spongepowered.api.util.SpongeApiTranslationHelper.t; @@ -34,6 +33,7 @@ import org.spongepowered.api.util.StartsWithPredicate; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -103,9 +103,11 @@ private boolean parseLongFlag(CommandSource source, String longFlag, CommandArgs case ERROR: throw args.createError(t("Unknown long flag %s specified", flagSplit[0])); case ACCEPT_NONVALUE: + context.addFlag(flag); context.putArg(flag, flagSplit.length == 2 ? flagSplit[1] : true); return true; case ACCEPT_VALUE: + context.addFlag(flag); context.putArg(flag, flagSplit.length == 2 ? flagSplit[1] : args.next()); return true; case IGNORE: @@ -134,9 +136,11 @@ private boolean parseShortFlags(CommandSource source, String shortFlags, Command case ERROR: throw args.createError(t("Unknown short flag %s specified", shortFlag)); case ACCEPT_NONVALUE: + context.addFlag(shortFlag); context.putArg(shortFlag, true); break; case ACCEPT_VALUE: + context.addFlag(shortFlag); context.putArg(shortFlag, args.next()); break; default: @@ -336,8 +340,6 @@ public static class Builder { Builder() {} - private static final Function MARK_TRUE_FUNC = input -> markTrue(Text.of(input)); - private Builder flag(Function func, String... specs) { final List availableFlags = new ArrayList<>(specs.length); CommandElement el = null; @@ -384,7 +386,7 @@ private Builder flag(Function func, String... specs) { * @return this */ public Builder flag(String... specs) { - return flag(MARK_TRUE_FUNC, specs); + return flag(input -> new FlagElement(Text.of(input), null), specs); } /** @@ -398,7 +400,7 @@ public Builder flag(String... specs) { * @return this */ public Builder permissionFlag(final String flagPermission, String... specs) { - return flag(input -> requiringPermission(markTrue(Text.of(input)), flagPermission), specs); + return flag(input -> requiringPermission(new FlagElement(Text.of(input), null), flagPermission), specs); } /** @@ -413,7 +415,7 @@ public Builder permissionFlag(final String flagPermission, String... specs) { * @return this */ public Builder valueFlag(CommandElement value, String... specs) { - return flag(ignore -> value, specs); + return flag(input -> new FlagElement(Text.of(input), value), specs); } /** @@ -490,4 +492,39 @@ public CommandElement buildWith(CommandElement wrapped) { this.unknownLongFlagBehavior, this.anchorFlags); } } + + private static class FlagElement extends CommandElement { + + @Nullable + private final CommandElement valueElement; + + private FlagElement(Text key, @Nullable CommandElement valueElement) { + super(key); + this.valueElement = valueElement; + } + + @Override + public void parse(CommandSource source, CommandArgs args, CommandContext context) throws ArgumentParseException { + String key = getUntranslatedKey(); + if (valueElement != null) { + valueElement.parse(source, args, context); + } else { + context.putArg(key, true); + } + context.addFlag(key); + } + + @Nullable + @Override + protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException { + return null; //unused + } + + @Override + public List complete(CommandSource src, CommandArgs args, CommandContext context) { + return valueElement != null ? valueElement.complete(src, args, context) : Collections.emptyList(); + } + + } + }