public static bool TryProcess(CommandInputSpecification spec, TParseResult parseResult, out DefaultCommandInput <TParseResult> result, out IReadOnlyList <CommandInputProcessingIssue> processingIssues) { spec = spec ?? throw new ArgumentNullException(nameof(spec)); List <CommandInputProcessingIssue> issues = null; List <InputElement> commandNameElements = null; foreach (IReadOnlyList <string> commandName in spec.CommandName) { if (TryProcessCommandName(commandName, parseResult, out List <InputElement> nameElements, out issues)) { commandNameElements = nameElements; break; } } if (commandNameElements is null) { result = null; processingIssues = issues; return(false); } List <InputElement> arguments = new List <InputElement>(); Dictionary <InputElement, InputElement> options = new Dictionary <InputElement, InputElement>(); InputElement currentOption = null; CommandOptionSpecification currentOptionSpec = null; InputElement selectedElement = null; for (int i = spec.CommandName.Count; i < parseResult.Sections.Count; ++i) { //If we're not looking at an option name if (!parseResult.Sections[i].StartsWith(spec.OptionPreamble.ToString(), StringComparison.OrdinalIgnoreCase) || parseResult.IsQuotedSection(i)) { if (currentOption is null) { InputElement currentElement = new InputElement(CommandInputLocation.Argument, parseResult.Sections[i], parseResult.Sections[i], i); if (i == parseResult.SelectedSection) { selectedElement = currentElement; } arguments.Add(currentElement); } else { //If the option isn't a defined one or it is and indicates that it accepts a value, add the section as an option value, // otherwise add it as an argument if (currentOptionSpec?.AcceptsValue ?? true) { InputElement currentElement = new InputElement(currentOption, CommandInputLocation.OptionValue, parseResult.Sections[i], parseResult.Sections[i], i); if (i == parseResult.SelectedSection) { selectedElement = currentElement; } options[currentOption] = currentElement; currentOption = null; currentOptionSpec = null; } else { InputElement currentElement = new InputElement(CommandInputLocation.Argument, parseResult.Sections[i], parseResult.Sections[i], i); if (i == parseResult.SelectedSection) { selectedElement = currentElement; } arguments.Add(currentElement); } } } //If we are looking at an option name else { //Otherwise, check to see whether the previous option had a required argument before committing it if (currentOption is object) { options[currentOption] = null; if (currentOptionSpec?.RequiresValue ?? false) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.MissingRequiredOptionInput, currentOption.Text)); } } CommandOptionSpecification optionSpec = spec.Options.FirstOrDefault(x => x.Forms.Any(y => string.Equals(y, parseResult.Sections[i], StringComparison.Ordinal))); if (optionSpec is null) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.UnknownOption, parseResult.Sections[i])); } currentOption = new InputElement(CommandInputLocation.OptionName, parseResult.Sections[i], optionSpec?.Id, i); if (i == parseResult.SelectedSection) { selectedElement = currentOption; } currentOptionSpec = optionSpec; } } //Clear any option in progress if (currentOption is object) { options[currentOption] = null; if (currentOptionSpec?.RequiresValue ?? false) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.MissingRequiredOptionInput, currentOption.Text)); } } //Check to make sure our argument count is in range, if not add an issue if (arguments.Count > spec.MaximumArguments || arguments.Count < spec.MinimumArguments) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.ArgumentCountOutOfRange, arguments.Count.ToString())); } //Build up the dictionary of options by normal form, then validate counts for every option in the spec Dictionary <string, IReadOnlyList <InputElement> > optionsByNormalForm = new Dictionary <string, IReadOnlyList <InputElement> >(StringComparer.Ordinal); foreach (KeyValuePair <InputElement, InputElement> entry in options) { if (entry.Key.NormalizedText is null) { continue; } if (!optionsByNormalForm.TryGetValue(entry.Key.NormalizedText, out IReadOnlyList <InputElement> rawBucket)) { optionsByNormalForm[entry.Key.NormalizedText] = rawBucket = new List <InputElement>(); } List <InputElement> bucket = (List <InputElement>)rawBucket; bucket.Add(entry.Value); } foreach (CommandOptionSpecification optionSpec in spec.Options) { if (!optionsByNormalForm.TryGetValue(optionSpec.Id, out IReadOnlyList <InputElement> values)) { optionsByNormalForm[optionSpec.Id] = values = new List <InputElement>(); } if (values.Count < optionSpec.MinimumOccurrences || values.Count > optionSpec.MaximumOccurrences) { issues.Add(new CommandInputProcessingIssue(CommandInputProcessingIssueKind.OptionUseCountOutOfRange, values.Count.ToString())); } } result = new DefaultCommandInput <TParseResult>(commandNameElements, arguments, optionsByNormalForm, selectedElement); processingIssues = issues; return(issues.Count == 0); }
protected abstract string GetHelpDetails(IShellState shellState, TProgramState programState, DefaultCommandInput <TParseResult> commandInput, TParseResult parseResult);
public IEnumerable <string> Suggest(IShellState shellState, TProgramState programState, TParseResult parseResult) { DefaultCommandInput <TParseResult> .TryProcess(InputSpec, parseResult, out DefaultCommandInput <TParseResult> commandInput, out IReadOnlyList <CommandInputProcessingIssue> _); string normalCompletionString = parseResult.SelectedSection == parseResult.Sections.Count ? string.Empty : parseResult.Sections[parseResult.SelectedSection].Substring(0, parseResult.CaretPositionWithinSelectedSection); //If we're completing in a name position, offer completion for the command name if (parseResult.SelectedSection < InputSpec.CommandName.Count) { IReadOnlyList <string> commandName = null; for (int j = 0; j < InputSpec.CommandName.Count; ++j) { bool success = true; for (int i = 0; i < parseResult.SelectedSection; ++i) { if (!string.Equals(InputSpec.CommandName[j][i], parseResult.Sections[i], StringComparison.OrdinalIgnoreCase)) { success = false; break; } } if (success) { commandName = InputSpec.CommandName[j]; break; } } if (commandName is null) { return(null); } if (commandName[parseResult.SelectedSection].StartsWith(normalCompletionString, StringComparison.OrdinalIgnoreCase)) { return(new[] { commandName[parseResult.SelectedSection] }); } } if (commandInput is null) { return(null); } if (normalCompletionString.StartsWith(InputSpec.OptionPreamble.ToString())) { return(GetOptionCompletions(commandInput, normalCompletionString)); } IEnumerable <string> completions = Enumerable.Empty <string>(); CommandInputLocation?inputLocation = commandInput.SelectedElement?.Location; if (inputLocation != CommandInputLocation.OptionValue && commandInput.Arguments.Count < InputSpec.MaximumArguments) { IEnumerable <string> results = GetArgumentSuggestionsForText(shellState, programState, parseResult, commandInput, normalCompletionString); if (results != null) { completions = results; } } switch (inputLocation) { case CommandInputLocation.OptionName: { IEnumerable <string> results = GetOptionCompletions(commandInput, normalCompletionString); if (results != null) { completions = completions.Union(results); } break; } case CommandInputLocation.OptionValue: { IEnumerable <string> results = GetOptionValueCompletions(shellState, programState, commandInput.SelectedElement.Owner.NormalizedText, commandInput, parseResult, normalCompletionString); if (results != null) { completions = completions.Union(results); } break; } case CommandInputLocation.Argument: { IEnumerable <string> argumentResults = GetArgumentSuggestionsForText(shellState, programState, parseResult, commandInput, normalCompletionString); if (argumentResults != null) { completions = completions.Union(argumentResults); } if (string.IsNullOrEmpty(normalCompletionString)) { IEnumerable <string> results = GetOptionCompletions(commandInput, normalCompletionString); if (results != null) { completions = completions.Union(results); } } break; } } return(completions); }
protected abstract Task ExecuteAsync(IShellState shellState, TProgramState programState, DefaultCommandInput <TParseResult> commandInput, TParseResult parseResult, CancellationToken cancellationToken);
protected virtual bool CanHandle(IShellState shellState, TProgramState programState, DefaultCommandInput <TParseResult> commandInput) { return(true); }
private IEnumerable <string> GetOptionCompletions(DefaultCommandInput <TParseResult> commandInput, string normalCompletionString) { return(InputSpec.Options.Where(x => commandInput.Options[x.Id].Count < x.MaximumOccurrences) .SelectMany(x => x.Forms) .Where(x => x.StartsWith(normalCompletionString, StringComparison.OrdinalIgnoreCase))); }
protected virtual IEnumerable <string> GetArgumentSuggestionsForText(IShellState shellState, TProgramState programState, TParseResult parseResult, DefaultCommandInput <TParseResult> commandInput, string normalCompletionString) { return(null); }
protected virtual IEnumerable <string> GetOptionValueCompletions(IShellState shellState, TProgramState programState, string optionId, DefaultCommandInput <TParseResult> commandInput, TParseResult parseResult, string normalizedCompletionText) { return(null); }