From 19ff8a839e6d7eae5b6013e1d73310c4d019e793 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Jun 2018 13:23:03 +0200 Subject: [PATCH 1/8] Add scripts/files as VS solution items --- T5.TextTemplating.sln | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/T5.TextTemplating.sln b/T5.TextTemplating.sln index e1159bd..88efd7d 100644 --- a/T5.TextTemplating.sln +++ b/T5.TextTemplating.sln @@ -9,6 +9,20 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "T5.TextTemplating.Tests", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TextTransform", "src\TextTransform\TextTransform.csproj", "{76F66FA6-639C-4689-9946-89A3E0DF76C5}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6D8E0F4D-3234-4970-BE37-3B5B28BCE386}" + ProjectSection(SolutionItems) = preProject + .travis.yml = .travis.yml + appveyor.yml = appveyor.yml + build.cmd = build.cmd + build.sh = build.sh + LICENSE = LICENSE + pack.cmd = pack.cmd + pack.sh = pack.sh + README.md = README.md + test.cmd = test.cmd + test.sh = test.sh + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 6f93925ad1ecbd676013e1c3abc3fbfc75f9ba39 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Tue, 12 Jun 2018 13:23:31 +0200 Subject: [PATCH 2/8] Fix version typo in README [ci skip] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4868472..35e624c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ a `` node to the project file, as shown below: ```xml + Version="1.1.0-*" /> ``` From 0de9b9e9a576f93dd17c0d0c33beb7380b2fd972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Vauchelles?= Date: Wed, 12 Sep 2018 21:13:20 +0200 Subject: [PATCH 3/8] Add parameter expansion when evaluating include path This is a squashed merge of PR #6. --- src/TextTemplating/TemplateGenerator.cs | 46 ++++++++++++++++++++++++- tests/EngineTests.cs | 33 ++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) diff --git a/src/TextTemplating/TemplateGenerator.cs b/src/TextTemplating/TemplateGenerator.cs index f752d73..c5d3ee7 100644 --- a/src/TextTemplating/TemplateGenerator.cs +++ b/src/TextTemplating/TemplateGenerator.cs @@ -251,6 +251,7 @@ protected virtual Type ResolveDirectiveProcessor (string processorName) protected virtual string ResolvePath (string path) { + path = ExpandParameters(path, parameters); path = Environment.ExpandEnvironmentVariables (path); if (Path.IsPathRooted (path)) return path; @@ -340,6 +341,49 @@ internal static bool TryParseParameter (string parameter, out string processor, return !string.IsNullOrEmpty (name); } + internal static string ExpandParameters(string value, Dictionary parameters) + { + const char TokenStart = '$'; + const char TokenOpen = '('; + const char TokenEnd = ')'; + + var sb = new StringBuilder(); + for (int i = 0; i < value.Length; ++i) + { + if (i < value.Length - 1 + && value[i] == TokenStart + && value[i + 1] == TokenOpen) + { + var endTokenIndex = i; + while (endTokenIndex < value.Length + && value[endTokenIndex] != TokenEnd) + ++endTokenIndex; + + if (endTokenIndex >= value.Length + || value[endTokenIndex] != TokenEnd) + { + // We reached the end of the string + // Probably not a token, or not closed token + sb.Append(value.Substring(i)); + break; + } + + var parameterName = value.Substring(i + 2, endTokenIndex - i - 2); + var key = new ParameterKey(string.Empty, string.Empty, parameterName); + if (parameters.TryGetValue(key, out string parameterValue)) + { + sb.Append(parameterValue); + } + else + sb.Append(value.Substring(i, endTokenIndex - i + 1)); + i = endTokenIndex; + } + else + sb.Append(value[i]); + } + return sb.ToString(); + } + protected virtual bool LoadIncludeText (string requestFileName, out string content, out string location) { content = ""; @@ -428,7 +472,7 @@ string ITextTemplatingEngineHost.TemplateFile { #endregion - struct ParameterKey : IEquatable + internal struct ParameterKey : IEquatable { public ParameterKey (string processorName, string directiveName, string parameterName) { diff --git a/tests/EngineTests.cs b/tests/EngineTests.cs index 674c479..649ba36 100644 --- a/tests/EngineTests.cs +++ b/tests/EngineTests.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using NUnit.Framework; namespace T5.TextTemplating.Tests @@ -68,5 +69,37 @@ public void ParameterParsing ( Assert.AreEqual (expectedName, name); Assert.AreEqual (expectedValue, value); } + +#pragma warning disable 414 + static object[] ParameterExpandCases = { + new object [] { "thisIsATest", "thisIsATest", new string[] { } }, + new object [] { "thisIs$(Not)ATest", "thisIs$(Not)ATest", new string[] { } }, + new object [] { "thisIs$(NotATest", "thisIs$(NotATest", new string[] { } }, + new object [] { "this$(T1)Is$(NotATest", "thisForSureIs$(NotATest", new string[] { "T1=ForSure" } }, + new object [] { "$(Not)thisIsATest", "NotAtAllthisIsATest", new string[] { "Not=NotAtAll" } }, + new object [] { "thisIs$(Not)ATest", "thisIsNotAtAllATest", new string[] { "Not=NotAtAll" } }, + new object [] { "thisIsATest$(Not)", "thisIsATestNotAtAll", new string[] { "Not=NotAtAll" } }, + new object [] { "this$(T1)IsA$(T2)Test$(T3)", "thisOneIsAnActualTestInTheEnd", new string[] { "T1=One", "T2=nActual", "T3=InTheEnd" } }, + }; + #pragma warning restore 414 + + [Test] + [TestCaseSource(nameof(ParameterExpandCases))] + public void ParameterExpand( + string toExpand, string expected, + string[] parametersString) + { + var parameters = new Dictionary(); + string processor, directive, name, value; + for (int i = 0; i < parametersString.Length; ++i) + { + var success = TemplateGenerator.TryParseParameter(parametersString[i], out processor, out directive, out name, out value); + Assert.True(success, $"Invalid test parameter input: {parametersString[i]}"); + parameters.Add(new TemplateGenerator.ParameterKey(processor, directive, name), value); + } + + var actual = TemplateGenerator.ExpandParameters(toExpand, parameters); + Assert.AreEqual(expected, actual); + } } } From b2366d58a20c4d372eaf4d9480b5a2ef31710806 Mon Sep 17 00:00:00 2001 From: Atif Aziz Date: Wed, 12 Dec 2018 09:54:03 +0100 Subject: [PATCH 4/8] Merge upstream changes to parameter directive handling This is a squashed merge of following changes from mono/t4: - mono/t4@3d5ac11103fb5fef3b1ae7eb8b00eb86752d2504 - mono/t4@086b6f941cc49c1736333b78cf001b0e748d362b - mono/t4@37ad9ac462e01981488b855d5a3e94e865955a97 > commit 3d5ac11103fb5fef3b1ae7eb8b00eb86752d2504 > Author: Mikayla Hutchinson > Date: Tue Dec 11 16:36:01 2018 -0500 > > Allow C# type names in parameter directives > > commit 086b6f941cc49c1736333b78cf001b0e748d362b > Author: Mikayla Hutchinson > Date: Tue Dec 11 16:09:29 2018 -0500 > > Simplify generated code when no appdomains or host property > > commit 37ad9ac462e01981488b855d5a3e94e865955a97 > Author: Mikayla Hutchinson > Date: Tue Dec 11 16:07:59 2018 -0500 > > Don't require type for parameter directives --- .../ParameterDirectiveProcessor.cs | 66 +++++++++++++++---- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs index fae1951..e1bbf20 100644 --- a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs +++ b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs @@ -102,14 +102,39 @@ public override bool IsDirectiveSupported (string directiveName) return directiveName == "parameter"; } + readonly Dictionary BuiltinTypesMap = new Dictionary { + { "bool", "System.Boolean" }, + { "byte", "System.Byte" }, + { "sbyte", "System.SByte" }, + { "char", "System.Char" }, + { "decimal", "System.Decimal" }, + { "double", "System.Double" }, + { "float ", "System.Single" }, + { "int", "System.Int32" }, + { "uint", "System.UInt32" }, + { "long", "System.Int64" }, + { "ulong", "System.UInt64" }, + { "object", "System.Object" }, + { "short", "System.Int16" }, + { "ushort", "System.UInt16" }, + { "string", "System.String" } + }; + public override void ProcessDirective (string directiveName, IDictionary arguments) { - string name = arguments["name"]; - string type = arguments["type"]; - if (string.IsNullOrEmpty (name)) + if (!arguments.TryGetValue ("name", out string name) || string.IsNullOrEmpty (name)) { throw new DirectiveProcessorException ("Parameter directive has no name argument"); - if (string.IsNullOrEmpty (type)) - throw new DirectiveProcessorException ("Parameter directive has no type argument"); + } + + if (!arguments.TryGetValue ("type", out string type)) { + type = "System.String"; + } else { + if (BuiltinTypesMap.TryGetValue (type, out string mappedType)) { + type = mappedType; + } else if (string.IsNullOrEmpty(type)) { + throw new DirectiveProcessorException ("Parameter directive empty type argument"); + } + } string fieldName = "_" + name + "Field"; var typeRef = new CodeTypeReference (type); @@ -127,25 +152,38 @@ public override void ProcessDirective (string directiveName, IDictionary Date: Wed, 12 Dec 2018 10:04:47 +0100 Subject: [PATCH 5/8] Revert "Merge upstream changes to parameter directive handling" This reverts commit b2366d58a20c4d372eaf4d9480b5a2ef31710806. --- .../ParameterDirectiveProcessor.cs | 66 ++++--------------- 1 file changed, 12 insertions(+), 54 deletions(-) diff --git a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs index e1bbf20..fae1951 100644 --- a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs +++ b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs @@ -102,39 +102,14 @@ public override bool IsDirectiveSupported (string directiveName) return directiveName == "parameter"; } - readonly Dictionary BuiltinTypesMap = new Dictionary { - { "bool", "System.Boolean" }, - { "byte", "System.Byte" }, - { "sbyte", "System.SByte" }, - { "char", "System.Char" }, - { "decimal", "System.Decimal" }, - { "double", "System.Double" }, - { "float ", "System.Single" }, - { "int", "System.Int32" }, - { "uint", "System.UInt32" }, - { "long", "System.Int64" }, - { "ulong", "System.UInt64" }, - { "object", "System.Object" }, - { "short", "System.Int16" }, - { "ushort", "System.UInt16" }, - { "string", "System.String" } - }; - public override void ProcessDirective (string directiveName, IDictionary arguments) { - if (!arguments.TryGetValue ("name", out string name) || string.IsNullOrEmpty (name)) { + string name = arguments["name"]; + string type = arguments["type"]; + if (string.IsNullOrEmpty (name)) throw new DirectiveProcessorException ("Parameter directive has no name argument"); - } - - if (!arguments.TryGetValue ("type", out string type)) { - type = "System.String"; - } else { - if (BuiltinTypesMap.TryGetValue (type, out string mappedType)) { - type = mappedType; - } else if (string.IsNullOrEmpty(type)) { - throw new DirectiveProcessorException ("Parameter directive empty type argument"); - } - } + if (string.IsNullOrEmpty (type)) + throw new DirectiveProcessorException ("Parameter directive has no type argument"); string fieldName = "_" + name + "Field"; var typeRef = new CodeTypeReference (type); @@ -152,38 +127,25 @@ public override void ProcessDirective (string directiveName, IDictionary Date: Wed, 12 Dec 2018 10:26:46 +0100 Subject: [PATCH 6/8] Emulate logical call context to support parameter directive Fixes #4 Co-authored-by: Daniel Cazzulino --- src/TextTemplating/CallContext.cs | 38 +++++++++++++++++++ .../ParameterDirectiveProcessor.cs | 2 +- src/TextTemplating/TemplateGenerator.cs | 1 + 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/TextTemplating/CallContext.cs diff --git a/src/TextTemplating/CallContext.cs b/src/TextTemplating/CallContext.cs new file mode 100644 index 0000000..786c54f --- /dev/null +++ b/src/TextTemplating/CallContext.cs @@ -0,0 +1,38 @@ +using System.Collections.Concurrent; +using System.Threading; + +namespace T5.TextTemplating +{ + // Credit http://www.cazzulino.com/callcontext-netstandard-netcore.html + + /// + /// Provides a way to set contextual data that flows with the call and + /// async context of a test or invocation. + /// + public static class CallContext + { + static ConcurrentDictionary> state = new ConcurrentDictionary>(); + + public static object LogicalGetData(string name) => + GetData(name); + + public static void LogicalSetData(string name, object data) => + SetData(name, data); + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item in the call context. + /// The object to store in the call context. + public static void SetData(string name, object data) => + state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; + + /// + /// Retrieves an object with the specified name from the . + /// + /// The name of the item in the call context. + /// The object in the call context associated with the specified name, or if not found. + public static object GetData(string name) => + state.TryGetValue(name, out AsyncLocal data) ? data.Value : null; + } +} diff --git a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs index fae1951..ad8ea33 100644 --- a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs +++ b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs @@ -131,7 +131,7 @@ public override void ProcessDirective (string directiveName, IDictionary Date: Wed, 12 Dec 2018 10:50:16 +0100 Subject: [PATCH 7/8] Revert "Emulate logical call context to support parameter directive" This reverts commit e95e7635021373716386d1efc55b0ce6ab5c868d due to a typo in the commit message that references the wrong issue (should have referenced issue #8, not issue #4). --- src/TextTemplating/CallContext.cs | 38 ------------------- .../ParameterDirectiveProcessor.cs | 2 +- src/TextTemplating/TemplateGenerator.cs | 1 - 3 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 src/TextTemplating/CallContext.cs diff --git a/src/TextTemplating/CallContext.cs b/src/TextTemplating/CallContext.cs deleted file mode 100644 index 786c54f..0000000 --- a/src/TextTemplating/CallContext.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Concurrent; -using System.Threading; - -namespace T5.TextTemplating -{ - // Credit http://www.cazzulino.com/callcontext-netstandard-netcore.html - - /// - /// Provides a way to set contextual data that flows with the call and - /// async context of a test or invocation. - /// - public static class CallContext - { - static ConcurrentDictionary> state = new ConcurrentDictionary>(); - - public static object LogicalGetData(string name) => - GetData(name); - - public static void LogicalSetData(string name, object data) => - SetData(name, data); - - /// - /// Stores a given object and associates it with the specified name. - /// - /// The name with which to associate the new item in the call context. - /// The object to store in the call context. - public static void SetData(string name, object data) => - state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; - - /// - /// Retrieves an object with the specified name from the . - /// - /// The name of the item in the call context. - /// The object in the call context associated with the specified name, or if not found. - public static object GetData(string name) => - state.TryGetValue(name, out AsyncLocal data) ? data.Value : null; - } -} diff --git a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs index ad8ea33..fae1951 100644 --- a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs +++ b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs @@ -131,7 +131,7 @@ public override void ProcessDirective (string directiveName, IDictionary Date: Wed, 12 Dec 2018 10:51:21 +0100 Subject: [PATCH 8/8] Emulate logical call context to support parameter directive Fixes #8 This is a re-application of e95e7635021373716386d1efc55b0ce6ab5c868d in order to correct the issue reference. Co-authored-by: Daniel Cazzulino --- src/TextTemplating/CallContext.cs | 38 +++++++++++++++++++ .../ParameterDirectiveProcessor.cs | 2 +- src/TextTemplating/TemplateGenerator.cs | 1 + 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 src/TextTemplating/CallContext.cs diff --git a/src/TextTemplating/CallContext.cs b/src/TextTemplating/CallContext.cs new file mode 100644 index 0000000..786c54f --- /dev/null +++ b/src/TextTemplating/CallContext.cs @@ -0,0 +1,38 @@ +using System.Collections.Concurrent; +using System.Threading; + +namespace T5.TextTemplating +{ + // Credit http://www.cazzulino.com/callcontext-netstandard-netcore.html + + /// + /// Provides a way to set contextual data that flows with the call and + /// async context of a test or invocation. + /// + public static class CallContext + { + static ConcurrentDictionary> state = new ConcurrentDictionary>(); + + public static object LogicalGetData(string name) => + GetData(name); + + public static void LogicalSetData(string name, object data) => + SetData(name, data); + + /// + /// Stores a given object and associates it with the specified name. + /// + /// The name with which to associate the new item in the call context. + /// The object to store in the call context. + public static void SetData(string name, object data) => + state.GetOrAdd(name, _ => new AsyncLocal()).Value = data; + + /// + /// Retrieves an object with the specified name from the . + /// + /// The name of the item in the call context. + /// The object in the call context associated with the specified name, or if not found. + public static object GetData(string name) => + state.TryGetValue(name, out AsyncLocal data) ? data.Value : null; + } +} diff --git a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs index fae1951..ad8ea33 100644 --- a/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs +++ b/src/TextTemplating/Microsoft.VisualStudio.TextTemplating/ParameterDirectiveProcessor.cs @@ -131,7 +131,7 @@ public override void ProcessDirective (string directiveName, IDictionary