3434import java .util .Optional ;
3535import java .util .Set ;
3636import java .util .concurrent .CompletableFuture ;
37+ import java .util .concurrent .Executor ;
3738import java .util .regex .Matcher ;
3839import java .util .regex .Pattern ;
3940import org .apiguardian .api .API ;
@@ -63,6 +64,10 @@ public final class CommandFlagParser<C> implements ArgumentParser.FutureArgument
6364 * Metadata for the last argument that was suggested
6465 */
6566 public static final CloudKey <String > FLAG_META_KEY = CloudKey .of ("__last_flag__" , TypeToken .get (String .class ));
67+ /**
68+ * Metadata for the position of the cursor before parsing the last flag's value
69+ */
70+ public static final CloudKey <Integer > FLAG_CURSOR_KEY = CloudKey .of ("__flag_cursor__" , TypeToken .get (Integer .class ));
6671 /**
6772 * Metadata for the set of parsed flags, used to detect duplicates.
6873 */
@@ -110,35 +115,35 @@ public CommandFlagParser(final @NonNull Collection<@NonNull CommandFlag<?>> flag
110115 *
111116 * @param commandContext Command context
112117 * @param commandInput The input arguments
118+ * @param completionExecutor The completion executor
113119 * @return current flag being typed, or {@code empty()} if none is
114120 */
115121 @ API (status = API .Status .STABLE )
116- public @ NonNull Optional <String > parseCurrentFlag (
122+ public @ NonNull CompletableFuture < Optional <String > > parseCurrentFlag (
117123 final @ NonNull CommandContext <@ NonNull C > commandContext ,
118- final @ NonNull CommandInput commandInput
124+ final @ NonNull CommandInput commandInput ,
125+ final @ NonNull Executor completionExecutor
119126 ) {
120127 /* If empty, nothing to do */
121128 if (commandInput .isEmpty ()) {
122- return Optional .empty ();
129+ return CompletableFuture . completedFuture ( Optional .empty () );
123130 }
124131
125- /* Before parsing, retrieve the last known input of the queue */
126132 final String lastInputValue = commandInput .lastRemainingToken ();
127133
128134 /* Parse, but ignore the result of parsing */
129135 final FlagParser parser = new FlagParser ();
130- parser .parse (commandContext , commandInput );
131-
132- /*
133- * If the parser parsed the entire queue, restore the last typed
134- * input obtained earlier.
135- */
136- if (commandInput .isEmpty ()) {
137- final int count = lastInputValue .length ();
138- commandInput .moveCursor (-count );
139- }
140-
141- return Optional .ofNullable (parser .lastParsedFlag ());
136+ final CompletableFuture <@ NonNull ArgumentParseResult <Object >> result = parser .parse (commandContext , commandInput );
137+
138+ return result .thenApplyAsync (parseResult -> {
139+ if (commandContext .contains (FLAG_CURSOR_KEY )) {
140+ commandInput .cursor (commandContext .get (FLAG_CURSOR_KEY ));
141+ } else if (parser .lastParsedFlag () == null && commandInput .isEmpty ()) {
142+ final int count = lastInputValue .length ();
143+ commandInput .moveCursor (-count );
144+ }
145+ return Optional .ofNullable (parser .lastParsedFlag ());
146+ }, completionExecutor );
142147 }
143148
144149 @ Override
@@ -485,6 +490,7 @@ private final class FlagParser {
485490
486491 // The flag has no argument, so we're done.
487492 if (flag .commandComponent () == null ) {
493+ commandContext .remove (FLAG_CURSOR_KEY );
488494 commandContext .flags ().addPresenceFlag (flag );
489495 parsedFlags .add (flag );
490496 return CompletableFuture .completedFuture (null );
@@ -515,12 +521,17 @@ private final class FlagParser {
515521
516522 // We then attempt to parse the flag.
517523 final CommandFlag parsingFlag = flag ;
524+ final CommandInput commandInputCopy = commandInput .copy ();
518525 return ((CommandComponent <C >) flag .commandComponent ())
519526 .parser ()
520527 .parseFuture (
521528 commandContext ,
522529 commandInput
523530 ).thenApply (parsedValue -> {
531+ if (parsedValue .failure ().isPresent () || commandInput .isEmpty () || commandInput .peek () != ' ' ) {
532+ commandContext .store (FLAG_CURSOR_KEY , commandInputCopy .cursor ());
533+ }
534+
524535 // Forward parsing errors.
525536 if (parsedValue .failure ().isPresent ()) {
526537 return (ArgumentParseResult <Object >) parsedValue ;
@@ -531,8 +542,12 @@ private final class FlagParser {
531542 // At this point we know the flag parsed successfully.
532543 parsedFlags .add (parsingFlag );
533544
534- // We're no longer parsing a flag.
535- this .lastParsedFlag = null ;
545+ if (!commandInput .isEmpty (false )) {
546+ if (commandInput .peek () == ' ' ) {
547+ // We're no longer parsing a flag.
548+ this .lastParsedFlag = null ;
549+ }
550+ }
536551
537552 return null ;
538553 });
@@ -548,7 +563,7 @@ private final class FlagParser {
548563 }
549564
550565 private @ NonNull CompletableFuture <ArgumentParseResult <Object >> fail (final @ NonNull Throwable exception ) {
551- return CompletableFuture . completedFuture ( ArgumentParseResult .failure (exception ) );
566+ return ArgumentParseResult .failureFuture (exception );
552567 }
553568 }
554569}
0 commit comments