Skip to content

Commit

Permalink
fixes #3804 implements search subcommand (#4054)
Browse files Browse the repository at this point in the history
* fixes #3804 implements search subcommand
* Args.Language implementation
* removed FilterDefinition.Name
  • Loading branch information
vlada-shubina committed Jan 11, 2022
1 parent b4358eb commit de6a53c
Show file tree
Hide file tree
Showing 39 changed files with 1,499 additions and 370 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
44 changes: 41 additions & 3 deletions src/Microsoft.TemplateEngine.Cli/Commands/BaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,24 @@ public override IEnumerable<string> 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;
}
Expand All @@ -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<FilterOptionDefinition, Option> SetupFilterOptions(IReadOnlyList<FilterOptionDefinition> filtersToSetup)
{
Dictionary<FilterOptionDefinition, Option> options = new Dictionary<FilterOptionDefinition, Option>();
foreach (var filterDef in filtersToSetup)
{
var newOption = GetFilterOption(filterDef);
this.AddOption(newOption);
options[filterDef] = newOption;
}
return options;
}

/// <summary>
/// Adds the tabular output settings options for the command from <paramref name="command"/>.
/// </summary>
protected void SetupTabularOutputOptions(ITabularOutputCommand command)
{
this.AddOption(command.ColumnsAllOption);
this.AddOption(command.ColumnsOption);
}

private static async Task<AsyncMutex?> EnsureEntryMutex(TArgs args, IEngineEnvironmentSettings environmentSettings, CancellationToken token)
{
// we don't need to acquire mutex in case of virtual settings
Expand Down
68 changes: 68 additions & 0 deletions src/Microsoft.TemplateEngine.Cli/Commands/BaseFilterableArgs.cs
Original file line number Diff line number Diff line change
@@ -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<FilterOptionDefinition, OptionResult> _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);
}

/// <summary>
/// Gets list of <see cref="FilterOptionDefinition"/> parsed from command.
/// </summary>
internal IEnumerable<FilterOptionDefinition> AppliedFilters => _filters.Keys;

/// <summary>
/// Gets value for filter <paramref name="filter"/>.
/// </summary>
/// <param name="filter"></param>
/// <returns>value of the filter.</returns>
/// <exception cref="ArgumentException">when <paramref name="filter"/> is not among <see cref="AppliedFilters"/>.</exception>
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>() ?? string.Empty;
}

/// <summary>
/// Gets token name used for filter <paramref name="filter"/>.
/// </summary>
/// <param name="filter"></param>
/// <returns>Token or null when token cannot be evaluated.</returns>
internal string? GetFilterToken(FilterOptionDefinition filter)
{
return _filters[filter].Token?.Value;
}

private static IReadOnlyDictionary<FilterOptionDefinition, OptionResult> ParseFilters(IFilterableCommand filterableCommand, ParseResult parseResult)
{
Dictionary<FilterOptionDefinition, OptionResult> filterValues = new();
foreach (var filter in filterableCommand.Filters)
{
OptionResult? value = parseResult.FindResultFor(filter.Value);
if (value != null)
{
filterValues[filter.Key] = value;
}
}
return filterValues;
}
}
}
126 changes: 126 additions & 0 deletions src/Microsoft.TemplateEngine.Cli/Commands/FilterOptionDefinition.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// 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.
/// </summary>
internal class FilterOptionDefinition
{
internal FilterOptionDefinition(Func<Option> optionFactory)
{
OptionFactory = optionFactory ?? throw new ArgumentNullException(nameof(optionFactory));
}

internal static FilterOptionDefinition AuthorFilter { get; } =
new TemplateFilterOptionDefinition(
optionFactory: () => SharedOptionsFactory.CreateAuthorOption(),
matchFilter: authorArg => WellKnownSearchFilters.AuthorFilter(authorArg),
mismatchCriteria: resolutionResult => resolutionResult.HasAuthorMismatch);

internal static FilterOptionDefinition BaselineFilter { get; } =
new TemplateFilterOptionDefinition(
optionFactory: () => SharedOptionsFactory.CreateBaselineOption(),
matchFilter: baselineArg => WellKnownSearchFilters.BaselineFilter(baselineArg),
mismatchCriteria: resolutionResult => resolutionResult.HasBaselineMismatch);

internal static FilterOptionDefinition LanguageFilter { get; } =
new TemplateFilterOptionDefinition(
optionFactory: () => SharedOptionsFactory.CreateLanguageOption(),
matchFilter: languageArg => WellKnownSearchFilters.LanguageFilter(languageArg),
mismatchCriteria: resolutionResult => resolutionResult.HasLanguageMismatch);

internal static FilterOptionDefinition TagFilter { get; } =
new TemplateFilterOptionDefinition(
optionFactory: () => SharedOptionsFactory.CreateTagOption(),
matchFilter: tagArg => WellKnownSearchFilters.ClassificationFilter(tagArg),
mismatchCriteria: resolutionResult => resolutionResult.HasClassificationMismatch);

internal static FilterOptionDefinition TypeFilter { get; } =
new TemplateFilterOptionDefinition(
optionFactory: () => SharedOptionsFactory.CreateTypeOption(),
matchFilter: typeArg => WellKnownSearchFilters.TypeFilter(typeArg),
mismatchCriteria: resolutionResult => resolutionResult.HasTypeMismatch);

internal static FilterOptionDefinition PackageFilter { get; } =
new PackageFilterOptionDefinition(
optionFactory: () => SharedOptionsFactory.CreatePackageOption(),
matchFilter: PackageMatchFilter);

/// <summary>
/// A predicate that creates instance of option.
/// </summary>
internal Func<Option> OptionFactory { get; }

private static Func<ITemplatePackageInfo, bool> PackageMatchFilter(string? packageArg)
{
return (pack) =>
{
if (string.IsNullOrWhiteSpace(packageArg))
{
return true;
}
return pack.Name.Contains(packageArg, StringComparison.OrdinalIgnoreCase);
};
}
}

/// <summary>
/// Defines supported dotnet new command filter option applicable to the template.
/// </summary>
internal class TemplateFilterOptionDefinition : FilterOptionDefinition
{
internal TemplateFilterOptionDefinition(
Func<Option> optionFactory,
Func<string?, Func<ITemplateInfo, MatchInfo?>> matchFilter,
Func<TemplateResolutionResult, bool> mismatchCriteria) : base(optionFactory)
{
TemplateMatchFilter = matchFilter ?? throw new ArgumentNullException(nameof(matchFilter));
MismatchCriteria = mismatchCriteria ?? throw new ArgumentNullException(nameof(mismatchCriteria));
}

/// <summary>
/// A predicate that returns the template match filter for the filter option.
/// Template match filter should return the MatchInfo for the given template based on filter value.
/// </summary>
/// <remarks>
/// Common template match filters are defined in Microsoft.TemplateEngine.Utils.WellKnonwnSearchFilter class.
/// </remarks>
internal Func<string?, Func<ITemplateInfo, MatchInfo?>> TemplateMatchFilter { get; set; }

/// <summary>
/// A predicate that returns if the filter option caused a mismatch in <see cref="TemplateResolutionResult"/> in case of partial match.
/// </summary>
internal Func<TemplateResolutionResult, bool> MismatchCriteria { get; set; }
}

/// <summary>
/// Defines supported dotnet new command filter option applicable to the package.
/// </summary>
internal class PackageFilterOptionDefinition : FilterOptionDefinition
{
internal PackageFilterOptionDefinition(
Func<Option> optionFactory,
Func<string?, Func<ITemplatePackageInfo, bool>> matchFilter) : base(optionFactory)
{
PackageMatchFilter = matchFilter ?? throw new ArgumentNullException(nameof(matchFilter));
}

/// <summary>
/// A predicate that returns the package match filter for the filter option
/// Package match filter should if package is a match based on filter value.
/// </summary>
internal Func<string?, Func<ITemplatePackageInfo, bool>> PackageMatchFilter { get; set; }
}
}
6 changes: 5 additions & 1 deletion src/Microsoft.TemplateEngine.Cli/Commands/GlobalArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public GlobalArgs(BaseCommand command, ParseResult parseResult)
DebugReinit = parseResult.GetValueForOption(command.DebugReinitOption);
DebugRebuildCache = parseResult.GetValueForOption(command.DebugRebuildCacheOption);
DebugShowConfig = parseResult.GetValueForOption(command.DebugShowConfigOption);
//TODO: check if it gets the command name correctly.
CommandName = GetNewCommandName(parseResult);
ParseResult = parseResult;
}
Expand All @@ -38,6 +37,11 @@ public GlobalArgs(BaseCommand command, ParseResult parseResult)

internal string? DebugCustomSettingsLocation { get; private set; }

protected static (bool, IReadOnlyList<string>?) ParseTabularOutputSettings(ITabularOutputCommand command, ParseResult parseResult)
{
return (parseResult.GetValueForOption(command.ColumnsAllOption), parseResult.GetValueForOption(command.ColumnsOption));
}

private string GetNewCommandName(ParseResult parseResult)
{
var command = parseResult.CommandResult.Command;
Expand Down
14 changes: 14 additions & 0 deletions src/Microsoft.TemplateEngine.Cli/Commands/IFilterableCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// 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;

namespace Microsoft.TemplateEngine.Cli.Commands
{
internal interface IFilterableCommand
{
IReadOnlyDictionary<FilterOptionDefinition, Option> Filters { get; }
}
}
23 changes: 23 additions & 0 deletions src/Microsoft.TemplateEngine.Cli/Commands/ITabularOutputCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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;

namespace Microsoft.TemplateEngine.Cli.Commands
{
internal interface ITabularOutputCommand
{
internal Option<bool> ColumnsAllOption { get; }

internal Option<IReadOnlyList<string>> ColumnsOption { get; }
}

internal interface ITabularOutputArgs
{
internal bool DisplayAllColumns { get; }

internal IReadOnlyList<string>? ColumnsToDisplay { get; }
}
}
4 changes: 2 additions & 2 deletions src/Microsoft.TemplateEngine.Cli/Commands/InstallCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ internal BaseInstallCommand(NewCommand parentCommand, ITemplateEngineHost host,
Arity = new ArgumentArity(1, 99)
};

internal virtual Option<bool> InteractiveOption { get; } = SharedOptionsFactory.GetInteractiveOption();
internal virtual Option<bool> InteractiveOption { get; } = SharedOptionsFactory.CreateInteractiveOption();

internal virtual Option<IReadOnlyList<string>> AddSourceOption { get; } = SharedOptionsFactory.GetAddSourceOption();
internal virtual Option<IReadOnlyList<string>> AddSourceOption { get; } = SharedOptionsFactory.CreateAddSourceOption();

protected NewCommand ParentCommand { get; }

Expand Down
Loading

0 comments on commit de6a53c

Please sign in to comment.