Skip to content

Commit

Permalink
Support flags in any position
Browse files Browse the repository at this point in the history
Signed-off-by: Pablete1234 <[email protected]>
  • Loading branch information
Pablete1234 committed Oct 14, 2022
1 parent 314f13a commit 430beb7
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 10 deletions.
39 changes: 31 additions & 8 deletions cloud-core/src/main/java/cloud/commandframework/CommandTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ private CommandTree(final @NonNull CommandManager<C> commandManager) {
commandContext.store(PARSING_ARGUMENT_KEY, i + 2);
}
}
} else if (child.getValue().getParser() instanceof FlagArgument.FlagArgumentParser) {
} else if (child.getValue().getParser() instanceof FlagArgument.FlagArgumentParser && child.isLeaf()) {

/*
* Use the flag argument parser to deduce what flag is being suggested right now
Expand Down Expand Up @@ -628,8 +628,7 @@ private CommandTree(final @NonNull CommandManager<C> commandManager) {
if (commandQueue.isEmpty()) {
return Collections.emptyList();
} else if (child.isLeaf() && commandQueue.size() < 2) {
commandContext.setCurrentArgument(child.getValue());
return child.getValue().getSuggestionsProvider().apply(commandContext, commandQueue.peek());
return directSuggestions(commandContext, child, commandQueue.peek());
} else if (child.isLeaf()) {
if (child.getValue() instanceof CompoundArgument) {
final String last = ((LinkedList<String>) commandQueue).getLast();
Expand All @@ -638,8 +637,7 @@ private CommandTree(final @NonNull CommandManager<C> commandManager) {
}
return Collections.emptyList();
} else if (commandQueue.peek().isEmpty()) {
commandContext.setCurrentArgument(child.getValue());
return child.getValue().getSuggestionsProvider().apply(commandContext, commandQueue.remove());
return directSuggestions(commandContext, child, commandQueue.peek());
}

// Store original input command queue before the parsers below modify it
Expand Down Expand Up @@ -670,8 +668,7 @@ private CommandTree(final @NonNull CommandManager<C> commandManager) {
commandQueue.addAll(commandQueueOriginal);

// Fallback: use suggestion provider of argument
commandContext.setCurrentArgument(child.getValue());
return child.getValue().getSuggestionsProvider().apply(commandContext, this.stringOrEmpty(commandQueue.peek()));
return directSuggestions(commandContext, child, commandQueue.peek());
}

private @NonNull String stringOrEmpty(final @Nullable String string) {
Expand All @@ -681,17 +678,42 @@ private CommandTree(final @NonNull CommandManager<C> commandManager) {
return string;
}

private @NonNull List<@NonNull String> directSuggestions(
final @NonNull CommandContext<C> commandContext,
final @NonNull Node<@NonNull CommandArgument<C, ?>> root,
final @NonNull String text) {
commandContext.setCurrentArgument(root.getValue());

List<String> suggestions = root.getValue().getSuggestionsProvider().apply(commandContext, text);
if (!(root.getValue() instanceof FlagArgument) || root.getChildren().isEmpty()) return suggestions;

suggestions = new ArrayList<>(suggestions);
for (final Node<CommandArgument<C, ?>> child : root.getChildren()) {
suggestions.addAll(child.getValue().getSuggestionsProvider().apply(commandContext, text));
}
return suggestions;
}

/**
* Insert a new command into the command tree
*
* @param command Command to insert
*/
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "ReferenceEquality"})
public void insertCommand(final @NonNull Command<C> command) {
synchronized (this.commandLock) {
Node<CommandArgument<C, ?>> node = this.internalTree;

List<CommandArgument<C, ?>> arguments = command.getArguments();

CommandArgument<C, ?> lastArgument = arguments.get(arguments.size() - 1);
FlagArgument<C> flags = lastArgument instanceof FlagArgument ? (FlagArgument<C>) lastArgument : null;

for (final CommandArgument<C, ?> argument : command.getArguments()) {
Node<CommandArgument<C, ?>> tempNode = node.getChild(argument);
if (flags != null && node != this.internalTree && argument != lastArgument)
node = node.addChild(flags);

if (tempNode == null) {
tempNode = node.addChild(argument);
} else if (argument instanceof StaticArgument && tempNode.getValue() != null) {
Expand All @@ -703,6 +725,7 @@ public void insertCommand(final @NonNull Command<C> command) {
node.children.sort(Comparator.comparing(Node::getValue));
}
tempNode.setParent(node);

node = tempNode;
}
if (node.getValue() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@ private class FlagParser {
this.currentFlagBeingParsed = Optional.empty();
this.currentFlagNameBeingParsed = Optional.empty();

if (!string.startsWith("-") && currentFlag == null) {
return ArgumentParseResult.success(FLAG_PARSE_RESULT_OBJECT);
}

/* Parse next flag name to set */
if (string.startsWith("-") && currentFlag == null) {
/* Remove flag argument from input queue */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ void testFlagYieldingGreedyStringFollowedByFlagArgument() {
);

// Assert
assertThat(suggestions1).containsExactly("hello");
assertThat(suggestions1).containsExactly("hello", "--flag", "--flag2", "-f");
assertThat(suggestions2).containsExactly("hello");
assertThat(suggestions3).containsExactly("--flag", "--flag2");
assertThat(suggestions4).containsExactly("--flag", "--flag2");
Expand Down Expand Up @@ -492,7 +492,7 @@ void testFlagYieldingStringArrayFollowedByFlagArgument() {
);

// Assert
assertThat(suggestions1).isEmpty();
assertThat(suggestions1).containsExactly("--flag", "--flag2", "-f");
assertThat(suggestions2).isEmpty();
assertThat(suggestions3).containsExactly("--flag", "--flag2");
assertThat(suggestions4).containsExactly("--flag", "--flag2");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// MIT License
//
// Copyright (c) 2021 Alexander Söderberg & Contributors
//
// 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 cloud.commandframework.feature;

import cloud.commandframework.CommandManager;
import cloud.commandframework.TestCommandSender;
import cloud.commandframework.arguments.standard.StringArgument;
import cloud.commandframework.execution.CommandResult;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static cloud.commandframework.util.TestUtils.createManager;
import static com.google.common.truth.Truth.assertThat;

class ArbitraryPositionFlagTest {

private CommandManager<TestCommandSender> commandManager;

@BeforeEach
void setup() {
this.commandManager = createManager();
}

@Test
void testParsingAllLocations() {
// Arrange
this.commandManager.command(
this.commandManager.commandBuilder("test")
.literal("param")
.argument(StringArgument.greedyFlagYielding("text"))
.flag(this.commandManager.flagBuilder("flag").withAliases("f")));

// Act
final CommandResult<TestCommandSender> result1 = this.commandManager.executeCommand(
new TestCommandSender(),
"test -f param foo bar"
).join();
final CommandResult<TestCommandSender> result2 = this.commandManager.executeCommand(
new TestCommandSender(),
"test param -f foo bar"
).join();
final CommandResult<TestCommandSender> result3 = this.commandManager.executeCommand(
new TestCommandSender(),
"test param foo bar -f"
).join();

// Assert
assertThat(result1.getCommandContext().flags().isPresent("flag")).isEqualTo(true);
assertThat(result2.getCommandContext().flags().isPresent("flag")).isEqualTo(true);
assertThat(result3.getCommandContext().flags().isPresent("flag")).isEqualTo(true);
}

}

0 comments on commit 430beb7

Please sign in to comment.