From c8919094a7ac3f08f02605a05a3bcce64171271c Mon Sep 17 00:00:00 2001 From: Vlada Shubina Date: Tue, 26 Oct 2021 13:07:59 +0300 Subject: [PATCH] fixes dotnet/templating#3804 implements search subcommand (#4054) * fixes dotnet/templating#3804 implements search subcommand * Args.Language implementation * removed FilterDefinition.Name --- .../CommandParsing/CommandParserSupport.cs | 4 +- .../Commands/BaseCommand.cs | 44 +++- .../Commands/BaseFilterableArgs.cs | 68 ++++++ .../Commands/FilterOptionDefinition.cs | 126 ++++++++++ .../Commands/GlobalArgs.cs | 6 +- .../Commands/IFilterableCommand.cs | 14 ++ .../Commands/ITabularOutputCommand.cs | 23 ++ .../Commands/InstallCommand.cs | 4 +- .../Commands/NewCommand.cs | 41 +++- .../Commands/SearchCommand.cs | 145 ++++++++++- .../Commands/SharedOptionsFactory.cs | 99 +++++++- .../Commands/UpdateCommand.cs | 4 +- .../TemplateInformationCoordinator.cs | 4 +- .../LocalizableStrings.Designer.cs | 63 +++-- .../LocalizableStrings.resx | 15 +- .../TabularOutput/CliTabularOutputSettings.cs | 4 + .../TemplateResolution/CliFilters.cs | 2 + .../TemplateSearch/CliSearchFiltersFactory.cs | 25 +- .../CliTemplateSearchCoordinator.cs | 104 +++++--- .../xlf/LocalizableStrings.cs.xlf | 35 ++- .../xlf/LocalizableStrings.de.xlf | 35 ++- .../xlf/LocalizableStrings.es.xlf | 35 ++- .../xlf/LocalizableStrings.fr.xlf | 35 ++- .../xlf/LocalizableStrings.it.xlf | 35 ++- .../xlf/LocalizableStrings.ja.xlf | 35 ++- .../xlf/LocalizableStrings.ko.xlf | 35 ++- .../xlf/LocalizableStrings.pl.xlf | 35 ++- .../xlf/LocalizableStrings.pt-BR.xlf | 35 ++- .../xlf/LocalizableStrings.ru.xlf | 35 ++- .../xlf/LocalizableStrings.tr.xlf | 35 ++- .../xlf/LocalizableStrings.zh-Hans.xlf | 35 ++- .../xlf/LocalizableStrings.zh-Hant.xlf | 35 ++- .../PublicAPI.Shipped.txt | 4 - .../PublicAPI.Unshipped.txt | 4 + .../WellKnownSearchFilters.cs | 12 +- .../ParserTests/InstallTests.cs | 2 +- .../ParserTests/SearchTests.cs | 223 +++++++++++++++++ .../TemplateSearchCoordinatorTests.cs | 146 ++++++----- test/dotnet-new3.UnitTests/DotnetNewSearch.cs | 228 ++++++++++++------ 39 files changed, 1499 insertions(+), 370 deletions(-) create mode 100644 src/Microsoft.TemplateEngine.Cli/Commands/BaseFilterableArgs.cs create mode 100644 src/Microsoft.TemplateEngine.Cli/Commands/FilterOptionDefinition.cs create mode 100644 src/Microsoft.TemplateEngine.Cli/Commands/IFilterableCommand.cs create mode 100644 src/Microsoft.TemplateEngine.Cli/Commands/ITabularOutputCommand.cs create mode 100644 test/Microsoft.TemplateEngine.Cli.UnitTests/ParserTests/SearchTests.cs diff --git a/src/Microsoft.TemplateEngine.Cli/CommandParsing/CommandParserSupport.cs b/src/Microsoft.TemplateEngine.Cli/CommandParsing/CommandParserSupport.cs index 4628fcd618..14e1ac2a93 100644 --- a/src/Microsoft.TemplateEngine.Cli/CommandParsing/CommandParserSupport.cs +++ b/src/Microsoft.TemplateEngine.Cli/CommandParsing/CommandParserSupport.cs @@ -59,10 +59,10 @@ private static Option[] NewCommandVisibleArgs Create.Option("-u|--uninstall", LocalizableStrings.UninstallHelp, Accept.ZeroOrMoreArguments()), Create.Option("--interactive", LocalizableStrings.OptionDescriptionInteractive, Accept.NoArguments()), Create.Option("--nuget-source|--add-source", LocalizableStrings.OptionDescriptionNuGetSource, Accept.OneOrMoreArguments()), - Create.Option("--type", LocalizableStrings.ShowsFilteredTemplates, Accept.ExactlyOneArgument()), + Create.Option("--type", LocalizableStrings.OptionDescriptionTypeFilter, Accept.ExactlyOneArgument()), Create.Option("--dry-run", LocalizableStrings.DryRunDescription, Accept.NoArguments()), Create.Option("--force", LocalizableStrings.ForcesTemplateCreation, Accept.NoArguments()), - Create.Option("-lang|--language", LocalizableStrings.LanguageParameter, Accept.ExactlyOneArgument()), + Create.Option("-lang|--language", LocalizableStrings.OptionDescriptionLanguageFilter, Accept.ExactlyOneArgument()), Create.Option("--update-check", LocalizableStrings.UpdateCheckCommandHelp, Accept.NoArguments()), Create.Option("--update-apply", LocalizableStrings.UpdateApplyCommandHelp, Accept.NoArguments()), Create.Option( diff --git a/src/Microsoft.TemplateEngine.Cli/Commands/BaseCommand.cs b/src/Microsoft.TemplateEngine.Cli/Commands/BaseCommand.cs index a1a4e427b1..4d67a87dfe 100644 --- a/src/Microsoft.TemplateEngine.Cli/Commands/BaseCommand.cs +++ b/src/Microsoft.TemplateEngine.Cli/Commands/BaseCommand.cs @@ -141,12 +141,24 @@ public override IEnumerable GetSuggestions(ParseResult? parseResult = nu return GetSuggestions(args, environmentSettings, textToMatch); } - protected static string? ValidateOptionUsageInParent(CommandResult symbolResult, Option option) + protected static string? ValidateOptionUsageInParent(CommandResult commandResult, Option option) { - OptionResult? optionResult = symbolResult.Parent?.Children.FirstOrDefault(symbol => symbol.Symbol == option) as OptionResult; + OptionResult? optionResult = commandResult.Parent?.Children.FirstOrDefault(symbol => symbol.Symbol == option) as OptionResult; if (optionResult != null) { - return $"Option '{optionResult.Token?.Value}' should be used after '{symbolResult.Symbol.Name}'."; + //Invalid command syntax: option '{0}' should be used after '{1}'. + return string.Format(LocalizableStrings.Commands_Validator_WrongOptionPosition, optionResult.Token?.Value, commandResult.Symbol.Name); + } + return null; + } + + protected static string? ValidateArgumentUsageInParent(CommandResult commandResult, Argument argument) + { + var newCommandArgument = commandResult.Parent?.Children.FirstOrDefault(symbol => symbol.Symbol == argument) as ArgumentResult; + if (newCommandArgument != null) + { + //Invalid command syntax: argument '{0}' should be used after '{1}'. + return string.Format(LocalizableStrings.Commands_Validator_WrongArgumentPosition, newCommandArgument.Tokens[0].Value, commandResult.Symbol.Name); } return null; } @@ -172,6 +184,32 @@ protected IEngineEnvironmentSettings CreateEnvironmentSettings(TArgs args) protected abstract TArgs ParseContext(ParseResult parseResult); + protected virtual Option GetFilterOption(FilterOptionDefinition def) + { + return def.OptionFactory(); + } + + protected IReadOnlyDictionary SetupFilterOptions(IReadOnlyList filtersToSetup) + { + Dictionary options = new Dictionary(); + foreach (var filterDef in filtersToSetup) + { + var newOption = GetFilterOption(filterDef); + this.AddOption(newOption); + options[filterDef] = newOption; + } + return options; + } + + /// + /// Adds the tabular output settings options for the command from . + /// + protected void SetupTabularOutputOptions(ITabularOutputCommand command) + { + this.AddOption(command.ColumnsAllOption); + this.AddOption(command.ColumnsOption); + } + private static async Task EnsureEntryMutex(TArgs args, IEngineEnvironmentSettings environmentSettings, CancellationToken token) { // we don't need to acquire mutex in case of virtual settings diff --git a/src/Microsoft.TemplateEngine.Cli/Commands/BaseFilterableArgs.cs b/src/Microsoft.TemplateEngine.Cli/Commands/BaseFilterableArgs.cs new file mode 100644 index 0000000000..cac2fd0608 --- /dev/null +++ b/src/Microsoft.TemplateEngine.Cli/Commands/BaseFilterableArgs.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.CommandLine.Parsing; + +namespace Microsoft.TemplateEngine.Cli.Commands +{ + internal class BaseFilterableArgs : GlobalArgs + { + private IReadOnlyDictionary _filters; + + internal BaseFilterableArgs(BaseCommand command, ParseResult parseResult) : base(command, parseResult) + { + if (command is not IFilterableCommand filterableCommand) + { + throw new ArgumentException($"{nameof(command)} should be {nameof(IFilterableCommand)}", nameof(command)); + } + _filters = ParseFilters(filterableCommand, parseResult); + } + + /// + /// Gets list of parsed from command. + /// + internal IEnumerable AppliedFilters => _filters.Keys; + + /// + /// Gets value for filter . + /// + /// + /// value of the filter. + /// when is not among . + internal string GetFilterValue(FilterOptionDefinition filter) + { + if (!_filters.ContainsKey(filter)) + { + throw new ArgumentException($"{nameof(filter)} is not available in parse result.", nameof(filter)); + } + + return _filters[filter].GetValueOrDefault() ?? string.Empty; + } + + /// + /// Gets token name used for filter . + /// + /// + /// Token or null when token cannot be evaluated. + internal string? GetFilterToken(FilterOptionDefinition filter) + { + return _filters[filter].Token?.Value; + } + + private static IReadOnlyDictionary ParseFilters(IFilterableCommand filterableCommand, ParseResult parseResult) + { + Dictionary filterValues = new(); + foreach (var filter in filterableCommand.Filters) + { + OptionResult? value = parseResult.FindResultFor(filter.Value); + if (value != null) + { + filterValues[filter.Key] = value; + } + } + return filterValues; + } + } +} diff --git a/src/Microsoft.TemplateEngine.Cli/Commands/FilterOptionDefinition.cs b/src/Microsoft.TemplateEngine.Cli/Commands/FilterOptionDefinition.cs new file mode 100644 index 0000000000..2660887151 --- /dev/null +++ b/src/Microsoft.TemplateEngine.Cli/Commands/FilterOptionDefinition.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable + +using System.CommandLine; +using Microsoft.TemplateEngine.Abstractions; +using Microsoft.TemplateEngine.Abstractions.TemplateFiltering; +using Microsoft.TemplateEngine.Cli.TemplateResolution; +using Microsoft.TemplateEngine.Utils; +using Microsoft.TemplateSearch.Common.Abstractions; + +namespace Microsoft.TemplateEngine.Cli.Commands +{ + /// + /// Defines supported dotnet new command filter option + /// Filter options can be used along with other dotnet new subcommands to filter the required items for the action, for example for list subcommand filters can limit the templates to be shown. + /// + internal class FilterOptionDefinition + { + internal FilterOptionDefinition(Func