Skip to content

Commit

Permalink
Feature/multichoices (#4732)
Browse files Browse the repository at this point in the history
* #4490 implementing support for quote-less choice literals (#4574)

* #4490 implementing support for quote-less choice literals

* Testfix

* Make the unquoted literals feature opt-in via EnableQuotelessLiterals property

* Localize error message

* Fix code and tests after merge

* Multichoice implementation contd (#4666)

* #4490 implementing support for quote-less choice literals

* #4490 multichoice parameters implementation

* Add Tab completion test cases

* Fix code + tests after merge

* Fix code and unit tests after merging

* Add support for multichoice parameters in join macro

* Improve based on review comments

* fix #4650 - adding validation for multichoice symbols (#4698)

* fix #4650 - adding validation for multichoice symbols

* Adjust error message

* Multichoice refactor quoteless (#4708)

* #4665 Refactoring part 1: Make EnableQuotelessLiterals exposed only to Orchestrator

* Fix parameters localization

* Fix after merge
  • Loading branch information
JanKrivanek authored May 13, 2022
1 parent 6c67d16 commit c82f2fd
Show file tree
Hide file tree
Showing 88 changed files with 1,651 additions and 139 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ public interface ITemplateParameter
/// </summary>
string? DisplayName { get; }

/// <summary>
/// Indicates whether parameter arity is allowed to be > 1.
/// </summary>
bool AllowMultipleValues { get; }

/// <summary>
/// Gets the default value to be used if the parameter is passed without value for template instantiation.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const Microsoft.TemplateEngine.Abstractions.TemplateFiltering.MatchInfo.BuiltIn.Constraint = "Constraint" -> string!
Microsoft.TemplateEngine.Abstractions.ITemplateParameter.AllowMultipleValues.get -> bool
Microsoft.TemplateEngine.Abstractions.ITemplateConstraint
Microsoft.TemplateEngine.Abstractions.ITemplateConstraint.DisplayName.get -> string!
Microsoft.TemplateEngine.Abstractions.ITemplateConstraint.Evaluate(string? args) -> Microsoft.TemplateEngine.Abstractions.TemplateConstraintResult!
Expand Down
70 changes: 43 additions & 27 deletions src/Microsoft.TemplateEngine.Cli/ChoiceTemplateParameter.cs
Original file line number Diff line number Diff line change
@@ -1,11 +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;
using System.CommandLine.Help;
using System.CommandLine.Parsing;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Cli.Commands;
using Microsoft.TemplateEngine.Utils;

namespace Microsoft.TemplateEngine.Cli
{
Expand All @@ -19,7 +22,7 @@ internal class ChoiceTemplateParameter : CliTemplateParameter

internal ChoiceTemplateParameter(ITemplateParameter parameter, HostSpecificTemplateData data) : base(parameter, data)
{
if (!parameter.DataType.Equals("choice", StringComparison.OrdinalIgnoreCase))
if (!parameter.IsChoice())
{
throw new ArgumentException($"{nameof(parameter)} should have {nameof(parameter.Type)} {nameof(ParameterType.Choice)}");
}
Expand All @@ -30,12 +33,6 @@ internal ChoiceTemplateParameter(ITemplateParameter parameter, HostSpecificTempl
_choices = parameter.Choices.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase);
}

internal ChoiceTemplateParameter(string name, IEnumerable<string>? shortNameOverrides = null, IEnumerable<string>? longNameOverrides = null)
: base(name, ParameterType.Choice, shortNameOverrides, longNameOverrides)
{
_choices = new Dictionary<string, ParameterChoice>(StringComparer.OrdinalIgnoreCase);
}

internal ChoiceTemplateParameter(ChoiceTemplateParameter choiceTemplateParameter)
: base(choiceTemplateParameter)
{
Expand Down Expand Up @@ -79,7 +76,7 @@ protected override Option GetBaseOption(IReadOnlyList<string> aliases)
aliases.ToArray(),
parseArgument: result => GetParseChoiceArgument(this)(result))
{
Arity = new ArgumentArity(DefaultIfOptionWithoutValue == null ? 1 : 0, 1)
Arity = new ArgumentArity(DefaultIfOptionWithoutValue == null ? 1 : 0, AllowMultipleValues ? _choices.Count : 1)
};

option.FromAmongCaseInsensitive(Choices.Keys.ToArray());
Expand Down Expand Up @@ -141,40 +138,59 @@ private static ParseArgument<string> GetParseChoiceArgument(ChoiceTemplateParame
argumentResult.ErrorMessage = string.Format(LocalizableStrings.ParseTemplateOption_Error_MissingDefaultIfNoOptionValue, or.Token?.Value);
return string.Empty;
}
else if (argumentResult.Tokens.Count == 1)
else if (!parameter.AllowMultipleValues && argumentResult.Tokens.Count != 1)
{
if (TryConvertValueToChoice(argumentResult.Tokens[0].Value, parameter, out string value, out string error))
{
return value;
}
//Cannot parse argument '{0}' for option '{1}' as expected type '{2}': {3}.
argumentResult.ErrorMessage = string.Format(
LocalizableStrings.ParseChoiceTemplateOption_Error_InvalidArgument,
argumentResult.Tokens[0].Value,
or.Token?.Value,
"choice",
error);
//Using more than 1 argument is not allowed for '{0}', used: {1}.
argumentResult.ErrorMessage = string.Format(LocalizableStrings.ParseTemplateOption_Error_InvalidCount, or.Token?.Value, argumentResult.Tokens.Count);
return string.Empty;
}
else
{
//Using more than 1 argument is not allowed for '{0}', used: {1}.
argumentResult.ErrorMessage = string.Format(LocalizableStrings.ParseTemplateOption_Error_InvalidCount, or.Token?.Value, argumentResult.Tokens.Count);
return string.Empty;
if (!TryConvertValueToChoice(argumentResult.Tokens.Select(t => t.Value), parameter, out string value, out string error))
{
//Cannot parse argument '{0}' for option '{1}' as expected type '{2}': {3}.
argumentResult.ErrorMessage = string.Format(
LocalizableStrings.ParseChoiceTemplateOption_Error_InvalidArgument,
argumentResult.Tokens[0].Value,
or.Token?.Value,
"choice",
error);
return string.Empty;
}

return value;
}
};
}

private static bool TryConvertValueToChoice(string value, ChoiceTemplateParameter parameter, out string parsedValue, out string error)
{
return TryConvertValueToChoice(value.TokenizeMultiValueParameter(), parameter, out parsedValue, out error);
}

private static bool TryConvertValueToChoice(IEnumerable<string> values, ChoiceTemplateParameter parameter, out string parsedValue, out string error)
{
parsedValue = string.Empty;
if (parameter.Choices == null)
error = string.Empty;

List<string> parsedValues = new List<string>();
foreach (string val in values)
{
//no choices are defined for parameter
error = LocalizableStrings.ParseChoiceTemplateOption_ErrorText_NoChoicesDefined;
return false;
if (!TryConvertSingleValueToChoice(val, parameter, out string value, out error))
{
return false;
}
parsedValues.Add(value);
}

parsedValue = string.Join(MultiValueParameter.MultiValueSeparator, parsedValues);
return true;
}

private static bool TryConvertSingleValueToChoice(string value, ChoiceTemplateParameter parameter, out string parsedValue, out string error)
{
parsedValue = string.Empty;

foreach (string choiceValue in parameter.Choices.Keys)
{
if (string.Equals(choiceValue, value, StringComparison.OrdinalIgnoreCase))
Expand Down
8 changes: 8 additions & 0 deletions src/Microsoft.TemplateEngine.Cli/CliTemplateParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ internal CliTemplateParameter(ITemplateParameter parameter, HostSpecificTemplate
IsRequired = parameter.Priority == TemplateParameterPriority.Required && parameter.DefaultValue == null;
IsHidden = parameter.Priority == TemplateParameterPriority.Implicit || data.HiddenParameterNames.Contains(parameter.Name);
AlwaysShow = data.ParametersToAlwaysShow.Contains(parameter.Name);
AllowMultipleValues = parameter.AllowMultipleValues;

if (data.ShortNameOverrides.ContainsKey(parameter.Name))
{
Expand Down Expand Up @@ -96,6 +97,7 @@ internal CliTemplateParameter(CliTemplateParameter other)
_shortNameOverrides = other.ShortNameOverrides.ToList();
_longNameOverrides = other.LongNameOverrides.ToList();
DefaultIfOptionWithoutValue = other.DefaultIfOptionWithoutValue;
AllowMultipleValues = other.AllowMultipleValues;

}

Expand All @@ -121,6 +123,8 @@ internal CliTemplateParameter(CliTemplateParameter other)

internal string? DefaultIfOptionWithoutValue { get; private set; }

protected bool AllowMultipleValues { get; private init; }

/// <summary>
/// Creates <see cref="Option"/> for template parameter.
/// </summary>
Expand Down Expand Up @@ -376,6 +380,10 @@ private string GetOptionDescription()
{
displayValue.AppendLine(string.Format(HelpStrings.RowHeader_Type, string.IsNullOrWhiteSpace(DataType) ? "string" : DataType));
}
if (AllowMultipleValues)
{
displayValue.AppendLine(string.Format(HelpStrings.RowHeader_AllowMultiValue, AllowMultipleValues));
}
//display the default value if there is one
if (!string.IsNullOrWhiteSpace(DefaultValue))
{
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/Microsoft.TemplateEngine.Cli/Commands/HelpStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@
<data name="Info_TemplateThirdPartyNotice" xml:space="preserve">
<value>This template contains technologies from parties other than Microsoft, see {0} for details.</value>
</data>
<data name="RowHeader_AllowMultiValue" xml:space="preserve">
<value>Allow multiple values: {0}</value>
<comment>{0} is a boolean flag</comment>
</data>
<data name="RowHeader_DefaultIfOptionWithoutValue" xml:space="preserve">
<value>Default if option is provided without a value: {0}</value>
<comment>{0} is default value if option was provided without a value</comment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Tato šablona obsahuje technologie jiných dodavatelů než Microsoftu, podrobnosti najdete tady: {0}</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Výchozí možnost, pokud se zadala možnost bez hodnoty: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Diese Vorlage enthält Technologien von anderen Anbietern als Microsoft, siehe {0} für Details.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Standardwert, wenn die Option ohne einen Wert bereitgestellt wird: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Esta plantilla contiene tecnologías de terceros ajenos a Microsoft, consulte {0} para obtener más información.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Valor predeterminado si se proporciona la opción sin un valor: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Ce modèle contient des technologies provenant d’autres parties que Microsoft, consultez {0} si vous souhaitez en savoir plus.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Valeur par défaut si l’option est fournie sans valeur : {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Questo modello contiene tecnologie provenienti da entità diverse da Microsoft, vedere {0} per i dettagli.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Impostazione predefinita se l'opzione è specificata senza un valore: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">このテンプレートには、Microsoft 以外のパーティのテクノロジが含まれています。詳しくは、{0} をご覧ください。</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">値なしで指定された場合の既定の if オプション: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">이 템플릿에는 Microsoft 이외의 타사 기술이 포함되어 있습니다. 자세한 내용은{0}를 참조하세요.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">옵션이 값 없이 제공되는 경우 기본값 :{0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Ten szablon zawiera technologie pochodzące od innych firm niż Microsoft; zobacz {0}, aby uzyskać szczegółowe informacje.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Wartość domyślna, jeśli opcja jest podana bez wartości: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Este modelo contém tecnologias de outras pessoas além da Microsoft, consulte {0} para obter detalhes.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Padrão se a opção for fornecida sem um valor: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Этот шаблон содержит технологии сторонних производителей, кроме Майкрософт. Дополнительные сведения см. в разделе {0}.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Значение по умолчанию, если параметр предоставлен без значения: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">Bu şablon Microsoft dışındaki tarafların teknolojilerini içeriyor. Ayrıntılar için {0} sayfasına bakın.</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">Seçenek bir değer olmadan sağlanırsa varsayılan: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">此模板包含除 Microsoft 以外其他方的技术,请参阅 {0} 以获取详细信息。</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">如果提供的选项没有值,则默认为: {0}</target>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
<target state="translated">此範本包含 Microsoft 其他協力廠商的技術,請參閱 {0} 取得詳細資訊。</target>
<note />
</trans-unit>
<trans-unit id="RowHeader_AllowMultiValue">
<source>Allow multiple values: {0}</source>
<target state="new">Allow multiple values: {0}</target>
<note>{0} is a boolean flag</note>
</trans-unit>
<trans-unit id="RowHeader_DefaultIfOptionWithoutValue">
<source>Default if option is provided without a value: {0}</source>
<target state="translated">如果提供的選項沒有值,則預設: {0}</target>
Expand Down
2 changes: 0 additions & 2 deletions src/Microsoft.TemplateEngine.Core.Contracts/IRunSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,5 @@ public interface IRunSpec
bool TryGetTargetRelPath(string sourceRelPath, out string targetRelPath);

IReadOnlyList<IOperationProvider> GetOperations(IReadOnlyList<IOperationProvider> sourceOperations);

IVariableCollection ProduceCollection(IVariableCollection parent);
}
}
Loading

0 comments on commit c82f2fd

Please sign in to comment.