public void InvalidUseOfGetCompletions() { var type = (TupleArgumentType)ArgumentType.GetType(typeof(Tuple <bool, bool>)); var c = new ArgumentCompletionContext { ParseContext = ArgumentParseContext.Default }; type.Invoking(t => t.GetCompletions(null, "Tr")).Should().Throw <ArgumentNullException>(); type.Invoking(t => t.GetCompletions(c, null)).Should().Throw <ArgumentNullException>(); }
/// <inheritdoc/> public IEnumerable <string> GetCompletions(ArgumentCompletionContext context, string valueToComplete) { // We get the entire command line here, including the token that triggered // the help command to be invoked. Any options to the help command would // also be present, which would pose a problem in the future if we add // more options to the help command. const int tokensToSkip = 1; return(_loop.GetCompletions(context.Tokens.Skip(tokensToSkip), context.TokenIndex - tokensToSkip)); }
public void GetCompletions() { var type = (TupleArgumentType)ArgumentType.GetType(typeof(Tuple <bool, int, bool>)); var c = new ArgumentCompletionContext { ParseContext = ArgumentParseContext.Default }; type.GetCompletions(c, "Tr").Should().Equal("True"); type.GetCompletions(c, string.Empty).Should().Equal("False", "True"); type.GetCompletions(c, "False,3").Should().BeEmpty(); type.GetCompletions(c, "False,3,").Should().Equal("False,3,False", "False,3,True"); }
/// <summary> /// Generate possible completions of this argument that start with the /// provided string prefix. /// </summary> /// <param name="tokens">The set of tokens in the input being completed. /// </param> /// <param name="indexOfTokenToComplete">The 0-based index of the token /// to complete.</param> /// <param name="valueToComplete">The prefix string.</param> /// <param name="inProgressParsedObject">Optionally, the object /// resulting from parsing and processing the tokens before the one /// being completed.</param> /// <returns>Possible completions.</returns> public IEnumerable <string> GetCompletions(IReadOnlyList <string> tokens, int indexOfTokenToComplete, string valueToComplete, object inProgressParsedObject) { var context = new ArgumentCompletionContext { ParseContext = ParseContext, Tokens = tokens, TokenIndex = indexOfTokenToComplete, InProgressParsedObject = inProgressParsedObject, CaseSensitive = ArgumentSet.Attribute.CaseSensitive }; return(Argument.ArgumentType.GetCompletions(context, valueToComplete)); }
public void GetCompletions() { var type = (KeyValuePairArgumentType)ArgumentType.GetType(typeof(KeyValuePair <bool, bool>)); var c = new ArgumentCompletionContext { ParseContext = ArgumentParseContext.Default }; type.GetCompletions(c, "Tr").Should().Equal("True"); type.GetCompletions(c, string.Empty).Should().Equal("False", "True"); type.GetCompletions(c, "False=f").Should().Equal("False=False"); type.GetCompletions(c, "33=f").Should().Equal("33=False"); type.GetCompletions(c, "True=").Should().Equal("True=False", "True=True"); }
public void GetCompletionsWithValidSeparators() { var type = (ArrayArgumentType)ArgumentType.GetType(typeof(bool[])); var c = new ArgumentCompletionContext { ParseContext = ArgumentParseContext.Default }; type.GetCompletions(c, "Tr").Should().Equal("True"); type.GetCompletions(c, string.Empty).Should().Equal("False", "True"); type.GetCompletions(c, "False,f").Should().Equal("False,False"); type.GetCompletions(c, "33,f").Should().Equal("33,False"); type.GetCompletions(c, "True,False,").Should().Equal("True,False,False", "True,False,True"); }
public void InvalidUseOfGetCompletions() { var type = (ArrayArgumentType)ArgumentType.GetType(typeof(bool[])); var c = new ArgumentCompletionContext { ParseContext = ArgumentParseContext.Default }; Action badContext = () => type.GetCompletions(null, "Tr"); badContext.Should().Throw <ArgumentNullException>(); Action badValue = () => type.GetCompletions(c, null); badValue.Should().Throw <ArgumentNullException>(); }
public void GetCompletionsWithoutValidSeparators() { var type = (ArrayArgumentType)ArgumentType.GetType(typeof(bool[])); var c = new ArgumentCompletionContext { ParseContext = new ArgumentParseContext { ElementSeparators = Array.Empty <string>() } }; type.GetCompletions(c, "Tr").Should().Equal("True"); type.GetCompletions(c, string.Empty).Should().Equal("False", "True"); type.GetCompletions(c, "False,f").Should().BeEmpty(); type.GetCompletions(c, "33,f").Should().BeEmpty(); type.GetCompletions(c, "True,False,").Should().BeEmpty(); }
public IEnumerable <string> GetCompletions(ArgumentCompletionContext context, string valueToComplete) => new[] { "|a|", "|ABC|" };
/// <summary> /// Generate possible completions for the specified token in the /// provided set of input tokens. /// </summary> /// <param name="tokens">The tokens.</param> /// <param name="indexOfTokenToComplete">Index of the token to complete. /// </param> /// <param name="destObjectFactory">If non-null, provides a factory /// function that can be used to create an object suitable to being /// filled out by this parser instance.</param> /// <returns>The candidate completions for the specified token. /// </returns> public IEnumerable <string> GetCompletions(IEnumerable <string> tokens, int indexOfTokenToComplete, Func <object> destObjectFactory) { Func <IEnumerable <string> > emptyCompletions = Enumerable.Empty <string>; var tokenList = tokens.ToList(); // Complain bitterly if we were asked to complete something far beyond // the token list we received. if (indexOfTokenToComplete > tokenList.Count) { throw new ArgumentOutOfRangeException(nameof(indexOfTokenToComplete)); } // If we were asked to complete the token just after the list we received, // then this means we're generating completions to append to the full // token list. Insert an empty token and use that as the basis for // completion. if (indexOfTokenToComplete == tokenList.Count) { tokenList = tokenList.Concat(new[] { string.Empty }).ToList(); } // Figure out the token we're going to complete. var tokenToComplete = tokenList[indexOfTokenToComplete]; // Create a destination object if provided with a factory. var inProgressParsedObject = destObjectFactory?.Invoke(); // Parse what we've seen thus far (before the token /to complete). // Note that this parse attempt may fail; we ignore success/failure. var tokensToParse = tokenList.Take(indexOfTokenToComplete).ToList(); var parseResult = ParseArgumentList(tokensToParse, inProgressParsedObject); // See if we're expecting this token to be an option argument; if // so, then we can generate completions based on that. if (parseResult.State == TokenParseResultState.RequiresOptionArgument && _argumentSet.Attribute.AllowNamedArgumentValueAsSucceedingToken) { return(parseResult.Argument.GetCompletions( tokenList, indexOfTokenToComplete, tokenToComplete, inProgressParsedObject)); } // See if the token to complete appears to be a (long-)named argument. var longNameArgumentPrefix = TryGetLongNameArgumentPrefix(tokenToComplete); if (longNameArgumentPrefix != null) { var afterPrefix = tokenToComplete.Substring(longNameArgumentPrefix.Length); return(GetNamedArgumentCompletions(tokenList, indexOfTokenToComplete, afterPrefix, inProgressParsedObject) .Select(completion => longNameArgumentPrefix + completion)); } // See if the token to complete appears to be a (short-)named argument. var shortNameArgumentPrefix = TryGetShortNameArgumentPrefix(tokenToComplete); if (shortNameArgumentPrefix != null) { var afterPrefix = tokenToComplete.Substring(shortNameArgumentPrefix.Length); return(GetNamedArgumentCompletions(tokenList, indexOfTokenToComplete, afterPrefix, inProgressParsedObject) .Select(completion => shortNameArgumentPrefix + completion)); } // See if the token to complete appears to be the special answer-file argument. var answerFileArgumentPrefix = TryGetAnswerFilePrefix(tokenToComplete); if (answerFileArgumentPrefix != null) { var filePath = tokenToComplete.Substring(answerFileArgumentPrefix.Length); var parseContext = new ArgumentParseContext { FileSystemReader = _options.FileSystemReader, ParserContext = _options.Context, CaseSensitive = _argumentSet.Attribute.CaseSensitive }; var completionContext = new ArgumentCompletionContext { ParseContext = parseContext, TokenIndex = indexOfTokenToComplete, Tokens = tokenList, InProgressParsedObject = inProgressParsedObject, CaseSensitive = _argumentSet.Attribute.CaseSensitive }; return(ArgumentType.FileSystemPath.GetCompletions(completionContext, filePath) .Select(completion => answerFileArgumentPrefix + completion)); } // At this point, assume it must be a positional argument. If it's not, then there's not much // that we can do. if (!_argumentSet.TryGetPositionalArgument(_nextPositionalArgIndexToParse, out ArgumentDefinition positionalArg)) { return(emptyCompletions()); } return(positionalArg.GetCompletions( tokenList, indexOfTokenToComplete, tokenToComplete, inProgressParsedObject)); }
/// <summary> /// Generates possible completions for the indicated argument token. /// </summary> /// <param name="tokens">Full list of tokens in context.</param> /// <param name="indexOfTokenToComplete">0-based index of token /// to complete; must either reference valid token in <paramref name="tokens"/> /// or the index of the next token that would follow the provided /// tokens.</param> /// <param name="destObjectFactory">Optionally provides a function /// that may be used to instantiate an object of the destination /// parse output type.</param> /// <returns>Possible completions for the token.</returns> public IEnumerable <string> GetCompletions(IEnumerable <string> tokens, int indexOfTokenToComplete, Func <object> destObjectFactory) { Func <IEnumerable <string> > emptyCompletions = Enumerable.Empty <string>; var tokenList = tokens.ToList(); // Complain bitterly if we were asked to complete something far beyond // the token list we received. if (indexOfTokenToComplete > tokenList.Count) { throw new ArgumentOutOfRangeException(nameof(indexOfTokenToComplete)); } // If we were asked to complete the token just after the list we received, // then this means we're generating completions to append to the full // token list. Insert an empty token and use that as the basis for // completion. if (indexOfTokenToComplete == tokenList.Count) { tokenList = tokenList.Concat(new[] { string.Empty }).ToList(); } // Figure out the token we're going to complete. var tokenToComplete = tokenList[indexOfTokenToComplete]; // Create a destination object if provided with a factory. var inProgressParsedObject = destObjectFactory?.Invoke(); // Parse what we've seen thus far (before the token /to complete). // Note that this parse attempt may fail; we ignore success/failure. var tokensToParse = tokenList.Take(indexOfTokenToComplete).ToList(); var parseResult = ParseArgumentList(tokensToParse, inProgressParsedObject); // See if we're expecting this token to be an option argument; if // so, then we can generate completions based on that. bool completeViaLastArg = false; if (parseResult.State == ArgumentSetParseResultType.RequiresOptionArgument && ArgumentSet.Attribute.AllowNamedArgumentValueAsSucceedingToken) { completeViaLastArg = true; } // See if we just finished parsing an argument that takes the rest // of the line. if (parseResult.LastSeenArg?.TakesRestOfLine ?? false) { completeViaLastArg = true; } // If we can complete via the last seen argument, then construct a parser // around the argument and generate the completions. if (completeViaLastArg) { var argParseState = new ArgumentParser(ArgumentSet, parseResult.LastSeenArg, _options, /*destination=*/ null); return(argParseState.GetCompletions( tokenList, indexOfTokenToComplete, tokenToComplete, inProgressParsedObject)); } // See if the token to complete appears to be a named argument. var longNameArgumentPrefix = TryGetLongNameArgumentPrefix(tokenToComplete, allowIncompleteToken: true); var shortNameArgumentPrefix = TryGetShortNameArgumentPrefix(tokenToComplete, allowIncompleteToken: true); if (longNameArgumentPrefix != null || shortNameArgumentPrefix != null) { var completions = Enumerable.Empty <string>(); if (longNameArgumentPrefix != null) { var prefixLen = Math.Min(longNameArgumentPrefix.Length, tokenToComplete.Length); var afterLongPrefix = tokenToComplete.Substring(prefixLen); completions = completions.Concat( GetNamedArgumentCompletions(ArgumentNameType.LongName, tokenList, indexOfTokenToComplete, afterLongPrefix, inProgressParsedObject) .Select(completion => longNameArgumentPrefix + completion)); } if (shortNameArgumentPrefix != null) { var prefixLen = Math.Min(shortNameArgumentPrefix.Length, tokenToComplete.Length); var afterShortPrefix = tokenToComplete.Substring(prefixLen); completions = completions.Concat( GetNamedArgumentCompletions(ArgumentNameType.ShortName, tokenList, indexOfTokenToComplete, afterShortPrefix, inProgressParsedObject) .Select(completion => shortNameArgumentPrefix + completion)); } return(completions); } // See if the token to complete appears to be the special answer-file argument. var answerFileArgumentPrefix = TryGetAnswerFilePrefix(tokenToComplete); if (answerFileArgumentPrefix != null) { var filePath = tokenToComplete.Substring(answerFileArgumentPrefix.Length); var parseContext = new ArgumentParseContext { FileSystemReader = _options.FileSystemReader, ParserContext = _options.Context, ServiceConfigurer = _options.ServiceConfigurer, CaseSensitive = ArgumentSet.Attribute.CaseSensitive }; var completionContext = new ArgumentCompletionContext { ParseContext = parseContext, TokenIndex = indexOfTokenToComplete, Tokens = tokenList, InProgressParsedObject = inProgressParsedObject, CaseSensitive = ArgumentSet.Attribute.CaseSensitive }; return(ArgumentType.FileSystemPath.GetCompletions(completionContext, filePath) .Select(completion => answerFileArgumentPrefix + completion)); } // At this point, assume it must be a positional argument. If it's not, then there's not much // that we can do. if (!ArgumentSet.TryGetPositionalArgument(NextPositionalArgIndexToParse, out ArgumentDefinition positionalArg)) { return(emptyCompletions()); } var parseState = new ArgumentParser(ArgumentSet, positionalArg, _options, /*destination=*/ null); return(parseState.GetCompletions( tokenList, indexOfTokenToComplete, tokenToComplete, inProgressParsedObject)); }