-
Notifications
You must be signed in to change notification settings - Fork 395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Preserve arguments after redirects #138
Conversation
If you will add a summary it will be better to understand your main message :) |
Sure. I think I also found a different but related problem with redirects, suggestions, and arguments, so I'll explain that here too. Summary:When suggesting arguments after a redirect, the When executing a command after a redirect, the These two situations handle arguments differently when redirects are present. I believe that both of these situations are bugs. The correct outcome should be that all arguments are present. Reproducing the 'bugs'To help explain this better, I created a demonstration here willkroboth@b1663e5 that looks like this: @Test
public void testCustomStuff() throws Exception {
System.out.println("Testing custom stuff");
SuggestionProvider<Object> monitorSuggestions = (context, builder) -> {
System.out.println(context.getArguments());
builder.suggest("a").suggest("b").suggest("c");
return builder.buildFuture();
};
ArgumentCommandNode<Object, String> end = argument("ending", StringArgumentType.word())
.suggests(monitorSuggestions)
.then(argument("lastArg", StringArgumentType.word())
.suggests(monitorSuggestions)
.executes(context -> {
System.out.println(context.getArguments());
return 1;
})
)
.build();
ArgumentCommandNode<Object, Integer> firstBranchEnding = argument("number", IntegerArgumentType.integer(1, 10)).then(end).build();
LiteralArgumentBuilder<Object> firstBranch = literal("low").then(firstBranchEnding);
LiteralArgumentBuilder<Object> secondBranch = literal("high").then(
argument("number", IntegerArgumentType.integer(11, 20))
.redirect(firstBranchEnding)
);
LiteralArgumentBuilder<Object> base = literal("base")
.then(firstBranch)
.then(secondBranch);
subject.register(base);
System.out.println(subject.getCompletionSuggestions(subject.parse("base low 5 ", source)).get());
System.out.println();
System.out.println(subject.getCompletionSuggestions(subject.parse("base low 5 a ", source)).get());
System.out.println();
System.out.println(subject.getCompletionSuggestions(subject.parse("base high 15 ", source)).get());
System.out.println();
System.out.println(subject.getCompletionSuggestions(subject.parse("base high 15 a ", source)).get());
System.out.println();
subject.execute("base low 5 a b", source);
System.out.println();
subject.execute("base high 15 a b", source);
System.out.println();
} When this test is run, this gets printed into the console: Testing custom stuff
// First test
{number=ParsedArgument{range=StringRange{start=9, end=10}, result=5}}
Suggestions{range=StringRange{start=11, end=11}, suggestions=[Suggestion{range=StringRange{start=11, end=11}, text='a', tooltip='null'}, Suggestion{range=StringRange{start=11, end=11}, text='b', tooltip='null'}, Suggestion{range=StringRange{start=11, end=11}, text='c', tooltip='null'}]}
{number=ParsedArgument{range=StringRange{start=9, end=10}, result=5}, ending=ParsedArgument{range=StringRange{start=11, end=12}, result=a}}
Suggestions{range=StringRange{start=13, end=13}, suggestions=[Suggestion{range=StringRange{start=13, end=13}, text='a', tooltip='null'}, Suggestion{range=StringRange{start=13, end=13}, text='b', tooltip='null'}, Suggestion{range=StringRange{start=13, end=13}, text='c', tooltip='null'}]}
// Second test
{number=ParsedArgument{range=StringRange{start=10, end=12}, result=15}}
Suggestions{range=StringRange{start=13, end=13}, suggestions=[Suggestion{range=StringRange{start=13, end=13}, text='a', tooltip='null'}, Suggestion{range=StringRange{start=13, end=13}, text='b', tooltip='null'}, Suggestion{range=StringRange{start=13, end=13}, text='c', tooltip='null'}]}
{number=ParsedArgument{range=StringRange{start=10, end=12}, result=15}}
Suggestions{range=StringRange{start=15, end=15}, suggestions=[Suggestion{range=StringRange{start=15, end=15}, text='a', tooltip='null'}, Suggestion{range=StringRange{start=15, end=15}, text='b', tooltip='null'}, Suggestion{range=StringRange{start=15, end=15}, text='c', tooltip='null'}]}
// Third test
{number=ParsedArgument{range=StringRange{start=9, end=10}, result=5}, ending=ParsedArgument{range=StringRange{start=11, end=12}, result=a}, lastArg=ParsedArgument{range=StringRange{start=13, end=14}, result=b}}
// Fourth test
{ending=ParsedArgument{range=StringRange{start=13, end=14}, result=a}, lastArg=ParsedArgument{range=StringRange{start=15, end=16}, result=b}} This output shows both 'bugs' in action. It probably needs an explanation though if you don't know what to look for. Structure of the exampleThe structure of my test command looks like this: literal base
-> then literal low
-> then argument number (integer from 0 to 10)
-> then argument ending (string that's just a single word)
suggests 'monitorSuggestions'
-> then argument lastArg (string that's just a single word)
suggests 'monitorSuggestions'
executes the only executor for this command
-> then literal high
-> then argument number (integer from 11 to 20)
redirects to base->low->number
SuggestionProvider<Object> monitorSuggestions = (context, builder) -> {
System.out.println(context.getArguments());
builder.suggest("a").suggest("b").suggest("c");
return builder.buildFuture();
}; And the
The problem with redirects, arguments, and suggestionsThe first test checks the suggestions for Simplified output:
The second test checks the suggestions for Simplified output:
Here, the The problem with redirects, arguments, and executesThe third test checks what happens when executing Simplified output:
The fourth test checks what happens when executing Simplified output:
Here, the I think that both of these behaviors are unintentional. It seems that arguments are not being handled properly when redirects (and forks) create child It looks like #140 moved some functionality around, so this PR currently conflicts with the main branch. I also recently realized there was a bug with the suggestions (thanks to JorelAli/CommandAPI#310), so I haven't figured out how to fix that yet. I'll try to figure something out though and update this PR soon. |
Recreated this PR as #142 |
This PR resolves #137.
Previously, when executing a
CommandContext
that has a childCommandContext
due to the parser encountering a redirected node, the arguments present in the original context would not be copied to the child context. In the example I presented in #137, the command structure looked like this:The bug I identified in #137 means that when executing the redirected branch (eg.
"command low 5 some text"
), anIllegalArgumentException
would be thrown since the executor expects the"number"
argument to be present in the childCommandContext
, which is used when executing the command. I expected that the command should run without error, with the value for the"number"
argument being5
. In contrast, the 'real' branch would always work as expected, with the"number"
argument present as given.This PR fixes this bug by adding and using the
CommandContext#copyWithArgumentsOf
method:In my example case, the
"number" -> value: 5
argument present in the originalCommandContext
would be copied to the childCommandContext
, so that it is present when the executor is run.I added tests that directly make sure the new
CommandContext#copyWithArgumentsOf
method works as expected. I want to add tests to make surecopyWithArgumentsOf
is being used correctly when executing commands, but I wasn't sure where to put a test like that. I wanted to make sure that I followed the code style of this repo. I'm not yet sure how the code base tests functionality like this, so I erred on the side of not adding those tests yet.If you'd like to discuss whether or not the 'bug' this PR resolves is actually intended behavior, I think those comments fit best as replies to the original issue, #137. I'm honestly not sure if my issue is intended behavior or not since I couldn't find any documentation on what node redirects are supposed to do. It seems most intuitive to me that the arguments should be preserved, hence why I went through the effort to make this PR :P.