Skip to content

Commit 3844eeb

Browse files
authored
Merge pull request #162 from athinaslime/switch-minestom-fix
fix minestom switch issues
2 parents 419f3de + b6a1bfb commit 3844eeb

3 files changed

Lines changed: 117 additions & 14 deletions

File tree

‎common/src/main/java/revxrsal/commands/util/Permutations.java‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static <A extends CommandActor> List<List<ParameterNode<A, Object>>> gene
3737
List<List<ParameterNode<A, Object>>> falsePermutations = new ArrayList<>();
3838

3939
permute(required, 0, truePermutations);
40-
permute(optional, 0, falsePermutations);
40+
permuteAllSubsets(optional, falsePermutations);
4141

4242
List<List<ParameterNode<A, Object>>> result = new ArrayList<>();
4343

@@ -63,4 +63,37 @@ private static <A extends CommandActor> void permute(List<ParameterNode<A, Objec
6363
Collections.swap(list, start, i);
6464
}
6565
}
66+
67+
/**
68+
* Generates every ordering of every subset of {@code list}, including the
69+
* empty subset. Every element of {@code list} is optional (a flag or a
70+
* switch), so it may either be omitted entirely or supplied alongside the
71+
* others in any order.
72+
*/
73+
private static <A extends CommandActor> void permuteAllSubsets(
74+
List<ParameterNode<A, Object>> list,
75+
List<List<ParameterNode<A, Object>>> result
76+
) {
77+
List<List<ParameterNode<A, Object>>> subsets = new ArrayList<>();
78+
generateSubsets(list, 0, new ArrayList<>(), subsets);
79+
for (List<ParameterNode<A, Object>> subset : subsets) {
80+
permute(subset, 0, result);
81+
}
82+
}
83+
84+
private static <A extends CommandActor> void generateSubsets(
85+
List<ParameterNode<A, Object>> list,
86+
int index,
87+
List<ParameterNode<A, Object>> current,
88+
List<List<ParameterNode<A, Object>>> result
89+
) {
90+
if (index == list.size()) {
91+
result.add(new ArrayList<>(current));
92+
return;
93+
}
94+
generateSubsets(list, index + 1, current, result);
95+
current.add(list.get(index));
96+
generateSubsets(list, index + 1, current, result);
97+
current.remove(current.size() - 1);
98+
}
6699
}

‎minestom/src/main/java/revxrsal/commands/minestom/hooks/MinestomCommandHooks.java‎

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,19 +171,24 @@ else if (node instanceof ParameterNode<A, ?> parameter) {
171171

172172
List<ParameterNode<A, Object>> flags = filter(command.parameters().values(), v -> v.isFlag() || v.isSwitch());
173173
for (List<ParameterNode<A, Object>> permutation : Permutations.generatePermutations(flags)) {
174-
List<Argument<?>> path = new ArrayList<>(arguments.size() + permutation.size());
175-
path.addAll(arguments);
174+
List<List<ArgumentColl>> formsPerParameter = new ArrayList<>(permutation.size());
176175
for (ParameterNode<A, Object> parameter : permutation) {
177176
if (parameter.isSwitch()) {
178-
ArgumentColl sw = ofSwitch(parameter);
179-
path.addAll(sw.arguments());
177+
formsPerParameter.add(ofSwitchForms(parameter));
180178
} else if (parameter.isFlag()) {
181-
ArgumentColl argumentColl = ofFlag(parameter);
182-
path.addAll(argumentColl.arguments());
179+
formsPerParameter.add(ofFlagForms(parameter));
183180
}
184181
}
185182

186-
minestomCommand.addSyntax(generateAction(command), path.toArray(Argument[]::new));
183+
// a switch/flag with a shorthand has two equivalent forms (e.g. --silent
184+
// and -s), each of which must be registered as its own Minestom syntax
185+
for (List<ArgumentColl> combination : cartesianProduct(formsPerParameter)) {
186+
List<Argument<?>> path = new ArrayList<>(arguments);
187+
for (ArgumentColl coll : combination) {
188+
path.addAll(coll.arguments());
189+
}
190+
minestomCommand.addSyntax(generateAction(command), path.toArray(Argument[]::new));
191+
}
187192
}
188193
// we have <= 4 flags, so we create all possible permutations
189194
}
@@ -218,14 +223,70 @@ private ArgumentColl ofSwitch(ParameterNode<A, ?> parameter) {
218223
}
219224

220225
private ArgumentColl ofFlag(ParameterNode<A, ?> parameter) {
221-
ArgumentLiteral first = new ArgumentLiteral(DispatcherSettings.LONG_FORMAT_PREFIX + parameter.flagName());
226+
return ofFlag(parameter, DispatcherSettings.LONG_FORMAT_PREFIX + parameter.flagName());
227+
}
228+
229+
private ArgumentColl ofFlag(ParameterNode<A, ?> parameter, String literal) {
230+
ArgumentLiteral first = new ArgumentLiteral(literal);
222231
first.setCallback(createCallback(parameter));
223232
return new ArgumentColl(
224233
first,
225234
toArgument(parameter)
226235
);
227236
}
228237

238+
/**
239+
* Returns every equivalent form a switch may be written as: its long form
240+
* (e.g. {@code --silent}), plus its shorthand form (e.g. {@code -s}) if
241+
* one is defined. Each form must be registered as its own Minestom syntax,
242+
* since {@link ArgumentLiteral} only matches a single fixed string.
243+
*/
244+
private List<ArgumentColl> ofSwitchForms(ParameterNode<A, ?> parameter) {
245+
List<ArgumentColl> forms = new ArrayList<>(2);
246+
forms.add(ofSwitch(parameter));
247+
Character shorthand = parameter.shorthand();
248+
if (shorthand != null) {
249+
forms.add(new ArgumentColl(
250+
new ArgumentLiteral(DispatcherSettings.SHORT_FORMAT_PREFIX + shorthand)
251+
));
252+
}
253+
return forms;
254+
}
255+
256+
/**
257+
* Returns every equivalent form a flag may be written as. See {@link #ofSwitchForms}.
258+
*/
259+
private List<ArgumentColl> ofFlagForms(ParameterNode<A, ?> parameter) {
260+
List<ArgumentColl> forms = new ArrayList<>(2);
261+
forms.add(ofFlag(parameter));
262+
Character shorthand = parameter.shorthand();
263+
if (shorthand != null) {
264+
forms.add(ofFlag(parameter, DispatcherSettings.SHORT_FORMAT_PREFIX + shorthand));
265+
}
266+
return forms;
267+
}
268+
269+
/**
270+
* Computes the cartesian product of the given dimensions, i.e. every
271+
* combination obtained by picking exactly one element from each dimension.
272+
*/
273+
private static List<List<ArgumentColl>> cartesianProduct(List<List<ArgumentColl>> dimensions) {
274+
List<List<ArgumentColl>> result = new ArrayList<>();
275+
result.add(new ArrayList<>());
276+
for (List<ArgumentColl> dimension : dimensions) {
277+
List<List<ArgumentColl>> next = new ArrayList<>();
278+
for (List<ArgumentColl> partial : result) {
279+
for (ArgumentColl choice : dimension) {
280+
List<ArgumentColl> combined = new ArrayList<>(partial);
281+
combined.add(choice);
282+
next.add(combined);
283+
}
284+
}
285+
result = next;
286+
}
287+
return result;
288+
}
289+
229290
/**
230291
* Generates a {@link CommandExecutor} that invokes the given command from
231292
* the Minestom-provided context

‎minestom/src/main/java/revxrsal/commands/minestom/util/MinestomUtils.java‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,24 +69,33 @@ public static <A extends MinestomCommandActor> void readIntoLampContext(
6969
for (ParameterNode<A, ?> parameter : executionContext.command().parameters().values()) {
7070
Object o;
7171
if (parameter.isSwitch()) {
72-
o = containsFlag(context, parameter.switchName());
72+
o = containsFlag(context, parameter, parameter.switchName());
7373
} else {
7474
o = context.get(parameter.name());
7575
}
7676
if (o != null)
7777
executionContext.addResolvedArgument(parameter.name(), o);
7878
else {
79-
if (parameter.isFlag() && containsFlag(context, parameter.flagName()))
79+
if (parameter.isFlag() && containsFlag(context, parameter, parameter.flagName()))
8080
throw new MissingArgumentException(parameter, parameter.command());
8181
Object def = parameter.parse(StringStream.createMutable(""), executionContext);
8282
executionContext.addResolvedArgument(parameter.name(), def);
8383
}
8484
}
8585
}
8686

87-
private static boolean containsFlag(CommandContext context, String name) {
88-
return context.has(DispatcherSettings.LONG_FORMAT_PREFIX + name)
89-
|| context.has(DispatcherSettings.SHORT_FORMAT_PREFIX + name);
87+
/**
88+
* Checks whether the given flag/switch was supplied, in either its long
89+
* form (e.g. {@code --silent}) or its shorthand form (e.g. {@code -s}).
90+
* The shorthand is a single character, not the full name, so it must be
91+
* looked up separately via {@link ParameterNode#shorthand()} rather than
92+
* by prefixing the full name with the short format prefix.
93+
*/
94+
private static boolean containsFlag(CommandContext context, ParameterNode<?, ?> parameter, String name) {
95+
if (context.has(DispatcherSettings.LONG_FORMAT_PREFIX + name))
96+
return true;
97+
Character shorthand = parameter.shorthand();
98+
return shorthand != null && context.has(DispatcherSettings.SHORT_FORMAT_PREFIX + shorthand);
9099
}
91100

92101
}

0 commit comments

Comments
 (0)