-
Notifications
You must be signed in to change notification settings - Fork 403
Description
Version
1.0.18
Description
This is basically what has been raised as concern in #86 (comment)
The current SuggestionsBuilder
code does not correctly handle cases where after lowercasing the string, the length respectively the character indices change.
For language-insensitive conversion (Locale.ROOT
) this can currently only occur for İ
(U+0130
; "Latin Capital Letter I with Dot Above"), see also Unicode Special Casing (but it could affect more code points in the future).
The effect of this is that command nodes and argument types which use SuggestionsBuilder.getRemainingLowerCase()
fail to provide command suggestions.
Affected built-in command nodes / argument types:
LiteralCommandNode
BoolArgumentType
Possible solutions
Manually trying to map between the source and the lowercased string is probably rather difficult, if not impossible.
Instead an alternative might be to use one of the Character
methods for lowercasing (toLowerCase(char)
or toLowerCase(int)
), which maintain a 1:1 relationship. However, that would require manually going through all characters / code points and converting them which is very likely less performant than the String.toLowerCase
methods.
Example (Minecraft)
Version: 21w17a
Type in chat:
/execute as @e[name="İ"]
❌ Notice how no suggestions are shown
Example (code)
public class BrigadierTest {
public static void main(String[] args) throws CommandSyntaxException {
List<CommandDispatcher<Object>> dispatchers = Arrays.asList(createWithLiteral(), createWithBool());
for (CommandDispatcher<Object> dispatcher : dispatchers) {
for (String s : Arrays.asList("'!'", "'\u0130'")) {
ParseResults<Object> parseResults = dispatcher.parse("command " + s + " ", null);
System.out.println(s + ": " + dispatcher.getCompletionSuggestions(parseResults).join());
}
}
}
private static CommandDispatcher<Object> createWithLiteral() {
CommandDispatcher<Object> dispatcher = new CommandDispatcher<>();
dispatcher.register(LiteralArgumentBuilder.literal("command")
.then(RequiredArgumentBuilder.argument("first", StringArgumentType.string()).executes(c -> {
return 1;
}).then(LiteralArgumentBuilder.literal("second")).executes(c -> {
return 1;
})));
return dispatcher;
}
private static CommandDispatcher<Object> createWithBool() {
CommandDispatcher<Object> dispatcher = new CommandDispatcher<>();
dispatcher.register(LiteralArgumentBuilder.literal("command")
.then(RequiredArgumentBuilder.argument("first", StringArgumentType.string()).executes(c -> {
return 1;
}).then(RequiredArgumentBuilder.argument("second", BoolArgumentType.bool()).executes(c -> {
return 1;
}))));
return dispatcher;
}
}
❌ When the first argument contains İ
(\u0130
) CommandDispatcher
does not provide any suggestions