Skip to content

Commit 5f08427

Browse files
JothayJamesAtClarityRdJNL
authored
Patch 1 - Additional Variables/Tokens Support (#5)
* Update TextTemplatingFileGeneratorCore.cs Adding more token replacements * Additional Variables Support * Removed redundant inherited IVsSingleFileGenerator * Removed redundant ToString * Generated and Verified common variables to be used in T4 templates (per the Macro modal in Build Events) * Reading environment variables from the system for replacements, like %UserProfile% (use as $(UserProfile)) * Refactored reference variables * Make variable dictionary case insensitive Co-authored-by: James Gray <[email protected]> Co-authored-by: RdJNL <[email protected]>
1 parent 1b86652 commit 5f08427

File tree

2 files changed

+174
-23
lines changed

2 files changed

+174
-23
lines changed

TextTemplatingCoreLib/TextTemplatingHelper.cs

+45-3
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,36 @@
33
// https://github.com/RdJNL/TextTemplatingCore //
44
//---------------------------------------------//
55
using System;
6+
using System.Collections;
67
using System.Collections.Generic;
78
using System.Diagnostics;
89
using System.IO;
910
using System.Linq;
1011
using System.Reflection;
1112
using System.Text;
13+
using System.Text.RegularExpressions;
1214

1315
namespace RdJNL.TextTemplatingCore.TextTemplatingCoreLib
1416
{
1517
public static class TextTemplatingHelper
1618
{
17-
public static IEnumerable<string> ProcessReferences(IEnumerable<string> references, string inputFileName)
19+
public static IEnumerable<string> ProcessReferences(IEnumerable<string> references, string inputFileName, IDictionary<string, string> variables = null)
1820
{
19-
string[] refs = references.ToArray();
21+
variables = variables != null
22+
? new Dictionary<string, string>(variables, StringComparer.InvariantCultureIgnoreCase)
23+
: new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
24+
AddEnvironmentVariables(variables);
2025

21-
return refs
26+
return references
27+
.Select(r =>
28+
{
29+
foreach( var v in variables )
30+
{
31+
r = Regex.Replace(r, Regex.Escape($"$({v.Key})"), v.Value.Replace("$", "$$"), RegexOptions.IgnoreCase);
32+
}
33+
34+
return r;
35+
})
2236
.Where(r => r != "System")
2337
.Select(r =>
2438
{
@@ -31,6 +45,34 @@ public static IEnumerable<string> ProcessReferences(IEnumerable<string> referenc
3145
});
3246
}
3347

48+
private static void AddEnvironmentVariables(IDictionary<string, string> variables)
49+
{
50+
// Handle variables like $(UserProfile) for pulling NuGet packages from the local storage folder
51+
foreach( DictionaryEntry ev in Environment.GetEnvironmentVariables() )
52+
{
53+
if( !(ev.Key is string key) || variables.ContainsKey(key) )
54+
{
55+
continue;
56+
}
57+
58+
string value;
59+
switch( ev.Value )
60+
{
61+
case null:
62+
value = "";
63+
break;
64+
case string s:
65+
value = s;
66+
break;
67+
default:
68+
value = ev.Value.ToString();
69+
break;
70+
}
71+
72+
variables.Add(key, value);
73+
}
74+
}
75+
3476
public static string ExecuteTemplate(string inputFileName, string templateCode, string[] references, out TemplateError[] errors)
3577
{
3678
string coreInputFile = null;

TextTemplatingFileGeneratorCore/TextTemplatingFileGeneratorCore.cs

+129-20
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// https://github.com/RdJNL/TextTemplatingCore //
44
//---------------------------------------------//
55
using System;
6+
using System.Collections;
67
using System.Collections.Generic;
78
using System.IO;
89
using System.Linq;
@@ -18,7 +19,7 @@
1819
namespace RdJNL.TextTemplatingCore.TextTemplatingFileGeneratorCore
1920
{
2021
[Guid(GENERATOR_GUID)]
21-
public sealed class TextTemplatingFileGeneratorCore : BaseTemplatedCodeGenerator, IVsSingleFileGenerator
22+
public sealed class TextTemplatingFileGeneratorCore : BaseTemplatedCodeGenerator
2223
{
2324
public const string GENERATOR_GUID = "85B769DE-38F5-4CBE-91AE-D0DFA431FE30";
2425
public const string GENERATOR_NAME = nameof(TextTemplatingFileGeneratorCore);
@@ -72,7 +73,7 @@ protected override string ProcessTemplate(string inputFileName, string inputFile
7273
}
7374
catch( Exception e )
7475
{
75-
GenerateError(false, $"Something went wrong processing the template '{inputFileName}': {e.ToString()}");
76+
GenerateError(false, $"Something went wrong processing the template '{inputFileName}': {e}");
7677
return ERROR_OUTPUT;
7778
}
7879
}
@@ -96,33 +97,129 @@ private void DetectExtensionDirective(string inputFileContent)
9697

9798
private IEnumerable<string> ProcessReferences(string[] references, string inputFileName)
9899
{
99-
ThreadHelper.ThrowIfNotOnUIThread();
100+
IDictionary<string, string> variables = GetReferenceVariables(inputFileName);
100101

101-
DTE dte = (DTE)GetService(typeof(DTE));
102+
IEnumerable<string> refs = references.Take(references.Length - 2);
102103

103-
Solution solution = dte?.Solution;
104-
Project project = solution?.FindProjectItem(inputFileName)?.ContainingProject;
105-
Configuration configuration = project?.ConfigurationManager.ActiveConfiguration;
104+
return TextTemplatingHelper.ProcessReferences(refs, inputFileName, variables);
105+
}
106106

107-
string solutionFile = solution?.FullName;
108-
string solutionDir = solutionFile != null ? Path.GetDirectoryName(solutionFile) + "\\" : "";
109-
string projectFile = project?.FullName;
110-
string projectDir = projectFile != null ? Path.GetDirectoryName(projectFile) + "\\" : "";
107+
private IDictionary<string, string> GetReferenceVariables(string inputFileName)
108+
{
109+
ThreadHelper.ThrowIfNotOnUIThread();
111110

112-
string outputPath = (string)configuration?.Properties.Item("OutputPath")?.Value;
113-
string outputFileName = (string)project?.Properties.Item("OutputFileName")?.Value;
111+
DTE dte = (DTE)GetService(typeof(DTE));
114112

113+
// Solution
114+
string solutionName = "";
115+
string solutionExt = "";
116+
string solutionFileName = "";
117+
string solutionDir = "";
118+
string solutionPath = "";
119+
120+
// Project
121+
string projectName = "";
122+
string projectExt = "";
123+
string projectFileName = "";
124+
string projectDir = "";
125+
string projectPath = "";
126+
127+
// Target
128+
string targetName = "";
129+
string targetExt = "";
130+
string targetFileName = "";
131+
string targetDir = "";
115132
string targetPath = "";
116-
if( projectDir != null && outputPath != null && outputFileName != null )
133+
134+
// Other
135+
string configurationName = "";
136+
string outDir = "";
137+
string platformName = "";
138+
string devEnvDir = "";
139+
140+
if( dte != null )
117141
{
118-
targetPath = projectDir + outputPath + outputFileName;
119-
}
142+
if( dte.FullName != null )
143+
{
144+
devEnvDir = AddTrailingSlash(Path.GetDirectoryName(dte.FullName));
145+
}
120146

121-
IEnumerable<string> refs = references
122-
.Take(references.Length - 2)
123-
.Select(r => r.Replace("$(SolutionDir)", solutionDir).Replace("$(ProjectDir)", projectDir).Replace("$(TargetPath)", targetPath));
147+
Solution solution = dte.Solution;
148+
if( solution != null )
149+
{
150+
solutionFileName = Path.GetFileName(solution.FileName);
151+
152+
string solutionFile = solution.FullName;
153+
if( solutionFile != null )
154+
{
155+
solutionPath = solutionFile;
156+
solutionDir = AddTrailingSlash(Path.GetDirectoryName(solutionFile));
157+
solutionName = Path.GetFileNameWithoutExtension(solution.FullName);
158+
solutionExt = Path.GetExtension(solution.FullName);
159+
}
160+
161+
Project project = solution.FindProjectItem(inputFileName)?.ContainingProject;
162+
if( project != null )
163+
{
164+
projectFileName = Path.GetFileName(project.FileName);
165+
166+
string projectFile = project.FullName;
167+
if( projectFile != null )
168+
{
169+
projectPath = projectFile;
170+
projectDir = AddTrailingSlash(Path.GetDirectoryName(projectFile));
171+
projectName = Path.GetFileNameWithoutExtension(project.FullName);
172+
projectExt = Path.GetExtension(project.FullName);
173+
}
174+
175+
Configuration configuration = project.ConfigurationManager.ActiveConfiguration;
176+
if( configuration != null )
177+
{
178+
configurationName = configuration.ConfigurationName;
179+
platformName = configuration.PlatformName;
180+
181+
string outputPath = (string)configuration.Properties.Item("OutputPath")?.Value;
182+
string outputFileName = (string)project.Properties.Item("OutputFileName")?.Value;
183+
if( outputPath != null && outputFileName != null )
184+
{
185+
targetFileName = outputFileName;
186+
targetName = Path.GetFileNameWithoutExtension(outputFileName);
187+
targetExt = Path.GetExtension(outputFileName);
188+
outDir = AddTrailingSlash(outputPath);
189+
targetDir = projectDir + outDir;
190+
targetPath = targetDir + targetFileName;
191+
}
192+
}
193+
}
194+
}
195+
}
124196

125-
return TextTemplatingHelper.ProcessReferences(refs, inputFileName);
197+
var variables = new Dictionary<string, string>
198+
{
199+
["SolutionName"] = solutionName, // "MySolution"
200+
["TargetName"] = targetName, // "MyProject"
201+
["ProjectName"] = projectName, // "11.T4.MyProject"
202+
["ConfigurationName"] = configurationName, // "Debug"
203+
["OutDir"] = outDir, // "bin\Debug\"
204+
["TargetExt"] = targetExt, // ".dll"
205+
["ProjectExt"] = projectExt, // ".csproj"
206+
["SolutionExt"] = solutionExt, // ".sln"
207+
["PlatformName"] = platformName, // "Any CPU"
208+
["DevEnvDir"] = devEnvDir, // "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\"
209+
210+
["TargetFileName"] = targetFileName, // "MyProject.dll"
211+
["SolutionFileName"] = solutionFileName, // "MySolution.sln"
212+
["ProjectFileName"] = projectFileName, // "11.T4.MyProject.csproj"
213+
["SolutionDir"] = solutionDir, // "C:\Data\Projects\My\"
214+
215+
["SolutionPath"] = solutionPath, // "C:\Data\Projects\My\MySolution.sln"
216+
["ProjectDir"] = projectDir, // "C:\Data\Projects\My\11.T4\11.T4.MyProject\"
217+
["ProjectPath"] = projectPath, // "C:\Data\Projects\My\11.T4\11.T4.MyProject\11.T4.MyProject.csproj"
218+
["TargetDir"] = targetDir, // "C:\Data\Projects\My\11.T4\11.T4.MyProject\bin\Debug\"
219+
["TargetPath"] = targetPath, // "C:\Data\Projects\My\11.T4\11.T4.MyProject\bin\Debug\MyProject.dll"
220+
};
221+
222+
return variables;
126223
}
127224

128225
protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
@@ -183,6 +280,18 @@ private void GenerateError(bool warning, string message, int line = 1, int colum
183280
GeneratorErrorCallback(warning, 0, message, line + 1, column + 1);
184281
}
185282

283+
private string AddTrailingSlash(string path)
284+
{
285+
if( path != null && !path.EndsWith(@"\") )
286+
{
287+
return path + @"\";
288+
}
289+
else
290+
{
291+
return path;
292+
}
293+
}
294+
186295
private sealed class TextTemplatingCallback : ITextTemplatingCallback
187296
{
188297
private readonly TextTemplatingFileGeneratorCore _generator;

0 commit comments

Comments
 (0)