cloud icon indicating copy to clipboard operation
cloud copied to clipboard

Greedy parsers cannot have functioning suggestions

Open bergerkiller opened this issue 3 years ago • 1 comments

There's a hard limitation that a parser can only provide suggestions for one argument being typed. This is because as input to suggestions, only a single String can be provided. When there are two or more parts in the command queue, it is unclear what part should be sent to the suggestions provider.

A greedy string parser might want to suggest words with spaces in them, while a string[] parser might want to suggest a word for every element.

Because suggestions aren't even asked right now (because it is not clear what should happen), there is no workable solution.

https://github.com/Incendo/cloud/blob/1.4.0-dev/cloud-core/src/main/java/cloud/commandframework/CommandTree.java#L602

CompoundArgument has some special logic tailored to it, but also assumes the last-typed 'element' is what needs suggestions. For greedy parsers that is not valid.

Changing String to Queue<String> for suggestions would make it in parity with parsers, but would also break api compatibility. I know no good solution for this.

bergerkiller avatar Dec 21 '20 23:12 bergerkiller

Non-breaking solutions

Option A (small behavior change for greedy parsers)

Make it so a greedy or flag-yielding-greedy parser, will receive a string that is concatenating the remaining input with spaces. Non-greedy parsers will not be affected, and the greedy parsers that do get affected were already broken or mostly non-functioning, so this could be an ok solution.

Option B (no behavior change)

The command context could have a property for the currently-parsing-queue, similar to how getRawInput is a thing, would allow the flexibility of using the remaining text for greedy-parser suggestions without compromising breaking behavior anywhere.

Workarround, add it to the context yourself in a preprocessor:

A way to work around this issue is to add the input queue to the command context yourself using a preprocessor, then this becomes available to your suggest method (note: for the method to be called you need flag-yielding greedy, and a dummy flag in the command even if it does nothing.:

Example:

  // Put in some file with constants
  public static final CloudKey<LinkedList<String>> INPUT_QUEUE =
      SimpleCloudKey.of("__someprefix_input_queue__", new TypeToken<LinkedList<String>>(){});

  // Do when setting-up the manager
  public void managerSetup() {
    manager.registerCommandPreProcessor(context ->
        context.getCommandContext().store(INPUT_QUEUE, context.getInputQueue()));
  }

  // Your parser, which can now suggest with the whole input queue!
  public @NonNull List<@NonNull String> suggestions(
      @NonNull CommandContext<CommandSender> context, @NonNull String input) {
    List<String> inputQueue = context.get(INPUT_QUEUE);
    // In one case, cloud will call .remove() instead of .peek(), add it back.
    if (inputQueue.isEmpty()) inputQueue.add(input);
    
    // Prepare some suggestions based on the input queue!
  }

Is there a chance either of the two non-breaking solutions fit the project? I'd be willing to make and PR either of those two solutions if they'll get accepted.

Pablete1234 avatar Oct 14 '22 02:10 Pablete1234