Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/7.0.2xx] refactoring bits from next macro generation without API changes #5591

Merged
merged 1 commit into from
Nov 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/Available-Symbols-Generators.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ In this sample three symbols are defined:


## Coalesce
Behaves like the C# `??` operator.
Behaves like the C# `??` operator. Note: the empty string value and default value of value type is treated as `null`.
The typical use of this generator is to check if the parameter was provided by user, otherwise set fallback generated value.

#### Parameters

Expand Down
4 changes: 4 additions & 0 deletions docs/Contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ We follow the same versioning as https://github.com/dotnet/sdk and release branc
| Development | *main* |
| Release | *release/** |

# Ways to contribute

- [create new value form](./contributing/how-to-create-new-value-form.md)

[Top](#top)
65 changes: 65 additions & 0 deletions docs/contributing/how-to-create-new-value-form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# How to create a new value form

Available value forms are described [here](../Runnable-Project-Templates---Value-Forms.md).
Value form represent the form of the parameter, usually related to specific casing.

We appreciate creating new value forms by the community.

Value forms follow the following syntax:
```json
{
"forms":
{
"nameOfTheForm":
{
"identifier": "name-unique", //type of value form; should be unique value
"param1": "value1", //key-value parameters for the form. The value may be JSON array or object, if more complicated configuration is needed.
"param2": "value2"
}
}
}
```

To create new value form, follow the following guideline:

1. Implement [`Microsoft.TemplateEngine.Orchestrator.RunnableProjects.ValueForms.IValueFormFactory`](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms/IValueFormFactory.cs) interface.

The existing implementation of value form are located in [`ValueForms`](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueForms) folder of `Microsoft.TemplateEngine.Orchestrator.RunnableProjects` projects.

The implementation should have:
- `Identifier` property - unique identifier of the form
- `Type` property - unique `string` name, matching generated symbol type
- `Create(string? name = null)` method - creates the form for default configuration
- `FromJObject(string name, JObject? configuration = null)` method - creates the form for json configuration

The `IValueForm` has `Identifier` property that matches factory `Identifier`, `Name` corresponding the name of the form in JSON and `Process` method that create the form for the passed `value`.

You may want to derive your own implementation from one of the base classes:
- `BaseValueFormFactory` - basic features
- `ActionableValueFormFactory`- the form that just does the action without configuration
- `ConfigurableValueFormFactory`- the form that performs the action and based on configuration
- `DependantValueFormFactory` - the form that needs other forms for processing

The very basic implementation may be:
```CSharp
internal class HelloValueForm : ActionableValueFormFactory
{
internal const string FormIdentifier = "hello";

public HelloValueForm() : base(FormIdentifier)
{
}

protected override string Process(string value) => $"Hello {value}!";
}
```

2. Once the value form is implemented, add it to [value form collection](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/ValueFormRegistry.cs).

3. [optional] Update [JSON schema](../../src/Microsoft.TemplateEngine.Orchestrator.RunnableProjects/Schemas/JSON/template.json) with new value form syntax.
If you do so, also add [a new test case](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/SchemaTests/) for testing the syntax.

4. Add unit tests for new implementation. Macro related unit tests are located in [this folder](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/ValueFormTests/).
For more complete scenario, consider adding the full template generation tests to [`RunnableProjectGeneratorTests.cs`](../../test/Microsoft.TemplateEngine.Orchestrator.RunnableProjects.UnitTests/RunnableProjectGeneratorTests.cs).

5. Update documentation in [docs folder](../Runnable-Project-Templates---Value-Forms.md).
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.TemplateEngine.Core.Expressions.Cpp2
{
public class Cpp2StyleEvaluatorDefinition : SharedEvaluatorDefinition<Cpp2StyleEvaluatorDefinition, Cpp2StyleEvaluatorDefinition.Tokens>
{
private static readonly Dictionary<Encoding, ITokenTrie> TokenCache = new Dictionary<Encoding, ITokenTrie>();
private static readonly Dictionary<Encoding, ITokenTrie> TokenCache = new();

public enum Tokens
{
Expand Down Expand Up @@ -80,7 +80,7 @@ protected override ITokenTrie GetSymbols(IProcessorState processor)
{
if (!TokenCache.TryGetValue(processor.Encoding, out ITokenTrie tokens))
{
TokenTrie trie = new TokenTrie();
TokenTrie trie = new();

//Logic
trie.AddToken(processor.Encoding.GetBytes("&&"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract class SharedEvaluatorDefinition<TSelf, TTokens>
where TSelf : SharedEvaluatorDefinition<TSelf, TTokens>, new()
where TTokens : struct
{
private static readonly TSelf Instance = new TSelf();
private static readonly TSelf Instance = new();
private static readonly IOperatorMap<Operators, TTokens> Map = Instance.GenerateMap();
private static readonly bool DereferenceInLiteralsSetting = Instance.DereferenceInLiterals;
private static readonly string NullToken = Instance.NullTokenValue;
Expand Down Expand Up @@ -90,10 +90,10 @@ public static bool EvaluateFromString(ILogger logger, string text, IVariableColl
/// <returns></returns>
public static bool EvaluateFromString(ILogger logger, string text, IVariableCollection variables, out string faultedMessage, HashSet<string> referencedVariablesKeys = null)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(text)))
using (MemoryStream res = new MemoryStream())
using (MemoryStream ms = new(Encoding.UTF8.GetBytes(text)))
using (MemoryStream res = new())
{
EngineConfig cfg = new EngineConfig(logger, variables);
EngineConfig cfg = new(logger, variables);
IProcessorState state = new ProcessorState(ms, res, (int)ms.Length, (int)ms.Length, cfg, NoOperationProviders);
int len = (int)ms.Length;
int pos = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public sealed class DerivedSymbol : BaseValueSymbol
{
internal const string TypeName = "derived";

internal DerivedSymbol(string name, string valueTransform, string valueSource) : base(name, null)
internal DerivedSymbol(string name, string valueTransform, string valueSource, string? replaces = null) : base(name, replaces)
{
if (string.IsNullOrWhiteSpace(valueTransform))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,26 @@ internal GeneratedSymbol(string name, string generator) : base(name, null)
{
throw new ArgumentException($"'{nameof(generator)}' cannot be null or whitespace.", nameof(generator));
}

Generator = generator;
}

internal GeneratedSymbol(string name, string generator, IReadOnlyDictionary<string, string> parameters, string? dataType = null) : this(name, generator)
{
DataType = dataType;
Parameters = parameters;
}

internal GeneratedSymbol(string name, JObject jObject) : base(jObject, name)
{
string? generator = jObject.ToString(nameof(Generator));
if (string.IsNullOrWhiteSpace(generator))
{
throw new TemplateAuthoringException(string.Format(LocalizableStrings.SymbolModel_Error_MandatoryPropertyMissing, name, GeneratedSymbol.TypeName, nameof(Generator).ToLowerInvariant()), name);
throw new TemplateAuthoringException(string.Format(LocalizableStrings.SymbolModel_Error_MandatoryPropertyMissing, name, TypeName, nameof(Generator).ToLowerInvariant()), name);
}

Generator = generator!;
DataType = jObject.ToString(nameof(DataType));
Parameters = jObject.ToJTokenStringDictionary(StringComparer.Ordinal, nameof(Parameters));
Parameters = jObject.ToJTokenStringDictionary(StringComparer.OrdinalIgnoreCase, nameof(Parameters));
}

/// <inheritdoc/>
Expand All @@ -56,6 +61,6 @@ internal GeneratedSymbol(string name, JObject jObject) : base(jObject, name)
/// - the key is a parameter name
/// - the value is a JSON value.
/// </summary>
public IReadOnlyDictionary<string, string> Parameters { get; internal init; } = new Dictionary<string, string>();
public IReadOnlyDictionary<string, string> Parameters { get; internal init; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects.ConfigModel
{
public class SymbolValueFormsModel
{
private SymbolValueFormsModel(IReadOnlyList<string> globalForms)
internal SymbolValueFormsModel(IReadOnlyList<string> globalForms)
{
GlobalForms = globalForms;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ public sealed class TemplateConfigModel
{
private const string NameSymbolName = "name";
private readonly ILogger? _logger;
private readonly Dictionary<string, IValueForm> _forms = SetupValueFormMapForTemplate();
private IReadOnlyDictionary<string, string> _tags = new Dictionary<string, string>();
private Dictionary<string, BaseSymbol> _symbols = new Dictionary<string, BaseSymbol>();
private Dictionary<string, BaseSymbol> _symbols = new();
private IReadOnlyList<PostActionModel> _postActions = new List<PostActionModel>();
private string? _author;
private string? _name;
Expand Down Expand Up @@ -141,7 +142,7 @@ private TemplateConfigModel(JObject source, ILogger? logger, string? baselineNam
}
}
}
foreach (var symbol in ImplicitBindSymbols)
foreach (BindSymbol symbol in ImplicitBindSymbols)
{
if (!symbols.ContainsKey(symbol.Name))
{
Expand All @@ -161,7 +162,7 @@ private TemplateConfigModel(JObject source, ILogger? logger, string? baselineNam

// Custom operations for specials
IReadOnlyDictionary<string, JToken> allSpecialOpsConfig = source.ToJTokenDictionary(StringComparer.OrdinalIgnoreCase, nameof(SpecialCustomOperations));
List<CustomFileGlobModel> specialCustomSetup = new List<CustomFileGlobModel>();
List<CustomFileGlobModel> specialCustomSetup = new();

foreach (KeyValuePair<string, JToken> globConfigKeyValue in allSpecialOpsConfig)
{
Expand All @@ -174,7 +175,7 @@ private TemplateConfigModel(JObject source, ILogger? logger, string? baselineNam

SpecialCustomOperations = specialCustomSetup;

List<TemplateConstraintInfo> constraints = new List<TemplateConstraintInfo>();
List<TemplateConstraintInfo> constraints = new();
foreach (JProperty prop in source.PropertiesOf(nameof(Constraints)))
{
if (prop.Value is not JObject obj)
Expand All @@ -190,7 +191,7 @@ private TemplateConfigModel(JObject source, ILogger? logger, string? baselineNam
continue;
}
obj.TryGetValue(nameof(TemplateConstraintInfo.Args), StringComparison.OrdinalIgnoreCase, out JToken? args);
constraints.Add(new TemplateConstraintInfo(type!, args.ToJSONString()));
constraints.Add(new TemplateConstraintInfo(type!, args?.ToString(Formatting.None)));
}
Constraints = constraints;
}
Expand Down Expand Up @@ -343,7 +344,7 @@ internal init
{
_symbols = value.ToDictionary(s => s.Name, s => s);
_symbols[NameSymbolName] = NameSymbol;
foreach (var symbol in ImplicitBindSymbols)
foreach (BindSymbol symbol in ImplicitBindSymbols)
{
if (!_symbols.ContainsKey(symbol.Name))
{
Expand All @@ -356,7 +357,17 @@ internal init
/// <summary>
/// Gets the list of forms defined in the template ("forms" JSON property).
/// </summary>
public IReadOnlyDictionary<string, IValueForm> Forms { get; internal init; } = new Dictionary<string, IValueForm>();
public IReadOnlyDictionary<string, IValueForm> Forms
{
get => _forms;
internal init
{
foreach (KeyValuePair<string, IValueForm> kvp in value)
{
_forms[kvp.Key] = kvp.Value;
}
}
}

/// <summary>
/// Gets the placeholder filename defined in the template ("placeholderFilename" JSON property).
Expand Down Expand Up @@ -494,9 +505,9 @@ private static BaseSymbol SetupDefaultNameSymbol(string? sourceName)
};
}

private static IReadOnlyDictionary<string, IValueForm> SetupValueFormMapForTemplate(JObject source)
private static Dictionary<string, IValueForm> SetupValueFormMapForTemplate(JObject? source = null)
{
Dictionary<string, IValueForm> formMap = new Dictionary<string, IValueForm>(StringComparer.Ordinal);
Dictionary<string, IValueForm> formMap = new(StringComparer.Ordinal);

// setup all the built-in default forms.
// name of the form is form identifier
Expand All @@ -506,6 +517,11 @@ private static IReadOnlyDictionary<string, IValueForm> SetupValueFormMapForTempl
formMap[builtInForm.Key] = builtInForm.Value.Create();
}

if (source == null)
{
return formMap;
}

// setup the forms defined by the template configuration.
// if any have the same name as a default, the default is overridden.
IReadOnlyDictionary<string, JToken> templateDefinedforms = source.ToJTokenDictionary(StringComparer.OrdinalIgnoreCase, nameof(Forms));
Expand All @@ -517,13 +533,12 @@ private static IReadOnlyDictionary<string, IValueForm> SetupValueFormMapForTempl
formMap[form.Key] = ValueFormRegistry.GetForm(form.Key, o);
}
}

return formMap;
}

private static IReadOnlyDictionary<string, IBaselineInfo> BaselineInfoFromJObject(IEnumerable<JProperty> baselineJProperties)
{
Dictionary<string, IBaselineInfo> allBaselines = new Dictionary<string, IBaselineInfo>();
Dictionary<string, IBaselineInfo> allBaselines = new();

foreach (JProperty property in baselineJProperties)
{
Expand All @@ -532,8 +547,8 @@ private static IReadOnlyDictionary<string, IBaselineInfo> BaselineInfoFromJObjec
continue;
}

var defaultOverrides = obj.Get<JObject>(nameof(Utils.BaselineInfo.DefaultOverrides))?.ToStringDictionary() ?? new Dictionary<string, string>();
BaselineInfo baseline = new BaselineInfo(defaultOverrides, obj.ToString(nameof(baseline.Description)));
IReadOnlyDictionary<string, string>? defaultOverrides = obj.Get<JObject>(nameof(Utils.BaselineInfo.DefaultOverrides))?.ToStringDictionary() ?? new Dictionary<string, string>();
BaselineInfo baseline = new(defaultOverrides, obj.ToString(nameof(baseline.Description)));
allBaselines[property.Name] = baseline;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ internal static EvaluatorType ParseEvaluatorName(string? name, EvaluatorType? @d
"C++" => EvaluatorType.CPP,
"MSBUILD" => EvaluatorType.MSBuild,
"VB" => EvaluatorType.VB,
_ => throw new TemplateAuthoringException($"Unrecognized evaluator: '{evaluatorName}'.", evaluatorName),
_ => throw new TemplateAuthoringException(string.Format(LocalizableStrings.EvaluatorSelector_Exception_UnknownEvaluator, evaluatorName)),
};
return evaluator;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

namespace Microsoft.TemplateEngine.Orchestrator.RunnableProjects
{
internal class GlobalRunConfig : IGlobalRunConfig
internal class GlobalRunConfig
{
public IReadOnlyList<IOperationProvider> Operations { get; init; } = Array.Empty<IOperationProvider>();

public IVariableConfig VariableSetup { get; init; } = VariableConfig.DefaultVariableSetup();

public IReadOnlyList<IMacroConfig> Macros { get; init; } = Array.Empty<IMacroConfig>();
public IReadOnlyList<IMacroConfig> GeneratedSymbolMacros { get; init; } = Array.Empty<IMacroConfig>();

public IReadOnlyList<IMacroConfig> ComputedMacros { get; init; } = Array.Empty<IMacroConfig>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ internal GlobalRunSpec(

if (configuration.SpecialOperationConfig != null)
{
foreach ((string glob, IGlobalRunConfig runConfig) in configuration.SpecialOperationConfig)
foreach ((string glob, GlobalRunConfig runConfig) in configuration.SpecialOperationConfig)
{
IReadOnlyList<IOperationProvider> specialOps = Array.Empty<IOperationProvider>();

Expand Down Expand Up @@ -94,7 +94,7 @@ internal void SetupFileSource(FileSourceMatchInfo source)
// If there are custom Conditional operations, don't include the default Conditionals.
//
// Note: we may need a more robust filtering mechanism in the future.
private IReadOnlyList<IOperationProvider> ResolveOperations(IGlobalRunConfig runConfig, IDirectory templateRoot, IVariableCollection variables)
private IReadOnlyList<IOperationProvider> ResolveOperations(GlobalRunConfig runConfig, IDirectory templateRoot, IVariableCollection variables)
{
IReadOnlyList<IOperationProvider> customOperations = SetupCustomOperations(runConfig.CustomOperations, templateRoot, variables);
IReadOnlyList<IOperationProvider> defaultOperations = SetupOperations(templateRoot.MountPoint.EnvironmentSettings, variables, runConfig);
Expand All @@ -113,7 +113,7 @@ private IReadOnlyList<IOperationProvider> ResolveOperations(IGlobalRunConfig run
return operations;
}

private IReadOnlyList<IOperationProvider> SetupOperations(IEngineEnvironmentSettings environmentSettings, IVariableCollection variables, IGlobalRunConfig runConfig)
private IReadOnlyList<IOperationProvider> SetupOperations(IEngineEnvironmentSettings environmentSettings, IVariableCollection variables, GlobalRunConfig runConfig)
{
// default operations
List<IOperationProvider> operations = new List<IOperationProvider>();
Expand Down

This file was deleted.

Loading