Skip to content

Commit

Permalink
Add defined flags to CommandContext (#2033)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
WillBAnders committed Jul 25, 2020
1 parent 7330bc2 commit 24c2f01
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand All @@ -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<String, Object> parsedArgs;
private final Set<String> definedFlags;

/**
* Create a new empty CommandContext.
*/
public CommandContext() {
this.parsedArgs = ArrayListMultimap.create();
this.definedFlags = Sets.newHashSet();
}

/**
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -261,9 +305,11 @@ public void applySnapshot(Snapshot snapshot) {
public final class Snapshot {

final Multimap<String, Object> args;
final Set<String> flags;

Snapshot(Multimap<String, Object> args) {
Snapshot(Multimap<String, Object> args, Set<String> flags) {
this.args = ArrayListMultimap.create(args);
this.flags = Sets.newHashSet(flags);
}

}
Expand Down
49 changes: 43 additions & 6 deletions src/main/java/org/spongepowered/api/command/args/CommandFlags.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -336,8 +340,6 @@ public static class Builder {

Builder() {}

private static final Function<String, CommandElement> MARK_TRUE_FUNC = input -> markTrue(Text.of(input));

private Builder flag(Function<String, CommandElement> func, String... specs) {
final List<String> availableFlags = new ArrayList<>(specs.length);
CommandElement el = null;
Expand Down Expand Up @@ -384,7 +386,7 @@ private Builder flag(Function<String, CommandElement> 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);
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -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<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
return valueElement != null ? valueElement.complete(src, args, context) : Collections.emptyList();
}

}

}

0 comments on commit 24c2f01

Please sign in to comment.