Backend/RiderPlugin/ForTea.RiderPlugin/TemplateProcessing/CodeGeneration/Converters/T4CSharpExecutableIntermediateConverter.cs (149 lines of code) (raw):
using GammaJul.ForTea.Core.Parsing.Ranges;
using GammaJul.ForTea.Core.TemplateProcessing;
using GammaJul.ForTea.Core.TemplateProcessing.CodeCollecting;
using GammaJul.ForTea.Core.TemplateProcessing.CodeGeneration;
using GammaJul.ForTea.Core.TemplateProcessing.CodeGeneration.Converters.ClassName;
using GammaJul.ForTea.Core.TemplateProcessing.CodeGeneration.Converters.GeneratorKind;
using GammaJul.ForTea.Core.Tree;
using JetBrains.Annotations;
using JetBrains.DocumentModel;
using JetBrains.ForTea.RiderPlugin.Psi.Resolve.Macros;
using JetBrains.ForTea.RiderPlugin.TemplateProcessing.CodeGeneration.Reference;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.Tree;
using JetBrains.ReSharper.Psi.Util;
using JetBrains.Util;
using JetBrains.Util.dataStructures.TypedIntrinsics;
namespace JetBrains.ForTea.RiderPlugin.TemplateProcessing.CodeGeneration.Converters
{
public sealed class T4CSharpExecutableIntermediateConverter : T4CSharpRealIntermediateConverter
{
[NotNull] private const string SuffixResource =
"GammaJul.ForTea.Core.Resources.TemplateBaseFullExecutableSuffix.cs.template";
[NotNull] private const string HostspecificSuffixResource =
"GammaJul.ForTea.Core.Resources.HostspecificTemplateBaseFullExecutableSuffix.cs.template";
[NotNull] private const string AssemblyRegisteringResource =
"GammaJul.ForTea.Core.Resources.AssemblyRegistering.cs.template";
[NotNull] private IT4ReferenceExtractionManager ReferenceExtractionManager { get; }
public T4CSharpExecutableIntermediateConverter(
[NotNull] IT4File file,
[NotNull] IT4ReferenceExtractionManager referenceExtractionManager
) : base(file, new T4ExecutableClassNameProvider(), new T4ExecutableGeneratorKind())
{
file.AssertContainsNoIncludeContext();
ReferenceExtractionManager = referenceExtractionManager;
}
// When creating executable, we reference JetBrains.TextTemplating,
// which already contains the definition for TextTransformation
protected override void AppendClasses(T4CSharpCodeGenerationIntermediateResult intermediateResult)
{
AppendMainContainer(intermediateResult);
AppendClass(intermediateResult);
}
protected override void AppendHost()
{
AppendIndent();
Result.AppendLine($"public virtual {T4TextTemplatingFQNs.HostInterface} Host {{ get; }}");
}
protected override bool ShouldAppendPragmaDirectives => true;
protected override void AppendConstructor(T4CSharpCodeGenerationIntermediateResult intermediateResult)
{
if (!intermediateResult.HasHost) return;
AppendIndent();
Result.Append($"public GeneratedTextTransformation({T4TextTemplatingFQNs.Lifetime} lifetime)");
AppendIndent();
Result.AppendLine("{");
PushIndent();
AppendHostInitialization();
PopIndent();
AppendIndent();
Result.AppendLine("}");
}
private void AppendHostInitialization()
{
AppendIndent();
Result.AppendLine($"Host = new {T4TextTemplatingFQNs.HostImpl}(");
PushIndent();
{
AppendIndent();
Result.AppendLine("lifetime,");
AppendIndent();
Result.AppendLine($"new {T4TextTemplatingFQNs.Macros}");
AppendIndent();
Result.AppendLine("{");
Result.AppendLine(GenerateExpandableMacros());
AppendIndent();
Result.AppendLine("},");
AppendIndent();
Result.Append("\"");
string path = File.PhysicalPsiSourceFile.GetLocation().FullPath;
Result.Append(StringLiteralConverter.EscapeToRegular(path));
Result.AppendLine("\",");
AppendIndent();
Result.AppendLine("this);");
}
PopIndent();
}
[NotNull]
private string GenerateExpandableMacros()
{
var projectFile = File.PhysicalPsiSourceFile.ToProjectFile();
if (projectFile == null) return "";
var resolver = File.GetSolution().GetComponent<IT4LightMacroResolver>();
var macros = resolver.ResolveAllLightMacros(projectFile);
return macros.AggregateString(",\n", (builder, pair) => builder
.Append("{\"")
.Append(StringLiteralConverter.EscapeToRegular(pair.Key))
.Append("\", \"")
.Append(StringLiteralConverter.EscapeToRegular(pair.Value))
.Append("\"}")
);
}
private void AppendMainContainer([NotNull] T4CSharpCodeGenerationIntermediateResult intermediateResult)
{
string resource = intermediateResult.HasHost ? HostspecificSuffixResource : SuffixResource;
var provider = new T4TemplateResourceProvider(resource);
string encoding = intermediateResult.Encoding ?? T4EncodingsManager.GetEncoding(File);
string suffix = provider.ProcessResource(ClassNameProvider.GeneratedClassName, encoding);
Result.Append(suffix);
AppendAssemblyRegistering();
// assembly registration code is part of main class,
// so resources do not include closing brace
Result.Append("}");
}
private void AppendAssemblyRegistering()
{
var provider = new T4TemplateResourceProvider(AssemblyRegisteringResource);
string references = GetReferences();
string registering = provider.ProcessResource(references);
Result.Append(registering);
}
[NotNull]
private string GetReferences() => ReferenceExtractionManager
.ExtractReferenceLocationsTransitive(File)
.AggregateString(",\n", (builder, it) => builder
.Append("{\"")
.Append(StringLiteralConverter.EscapeToRegular(it.FullName))
.Append("\", \"")
.Append(StringLiteralConverter.EscapeToRegular(it.Location.FullPath))
.Append("\"}"));
protected override string GetTransformTextOverridabilityModifier(bool hasCustomBaseClass) => OverrideKeyword;
#region IT4ElementAppendFormatProvider
public override string ToStringConversionPrefix =>
T4TextTemplatingFQNs.ToStringHelper + ".ToStringWithCulture(";
public override bool ShouldBreakExpressionWithLineDirective => true;
public override void AppendMappedIfNeeded(T4CSharpCodeGenerationResult destination, IT4Code code) =>
destination.Append(code.GetText());
public override void AppendCompilationOffset(T4CSharpCodeGenerationResult destination, IT4TreeNode node)
{
var documentOffset = T4UnsafeManualRangeTranslationUtil.GetDocumentStartOffset(node);
IDocument tempQualifier = node
.FindLogicalPsiSourceFile()
.Document;
var lineOffset = documentOffset.ToDocumentCoords()
.Column;
for (var i = Int32<DocColumn>.O; i < lineOffset; i++)
{
destination.Append(" ");
}
}
#endregion IT4ElementAppendFormatProvider
}
}