public abstract void EndWriterScope(CodeRenderingContext context);
public abstract void WriteTemplate(CodeRenderingContext context, TemplateIntermediateNode node);
public sealed override void EndWriterScope(CodeRenderingContext context) { throw new NotImplementedException(nameof(EndWriterScope)); }
public abstract void WriteComponentChildContent(CodeRenderingContext context, ComponentChildContentIntermediateNode node);
public abstract void WriteHtmlBlock(CodeRenderingContext context, HtmlBlockIntermediateNode node);
public void WriteTagHelperHtmlAttribute(CodeRenderingContext context, DefaultTagHelperHtmlAttributeIntermediateNode node) { if (context.Parent as TagHelperIntermediateNode == null) { var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode)); throw new InvalidOperationException(message); } if (context.Options.DesignTime) { context.RenderChildren(node); } else { var attributeValueStyleParameter = $"{HtmlAttributeValueStyleTypeName}.{node.AttributeStructure}"; var isConditionalAttributeValue = node.Children.Any( child => child is CSharpExpressionAttributeValueIntermediateNode || child is CSharpCodeAttributeValueIntermediateNode); // All simple text and minimized attributes will be pre-allocated. if (isConditionalAttributeValue) { // Dynamic attribute value should be run through the conditional attribute removal system. It's // unbound and contains C#. // TagHelper attribute rendering is buffered by default. We do not want to write to the current // writer. var valuePieceCount = node.Children.Count( child => child is HtmlAttributeValueIntermediateNode || child is CSharpExpressionAttributeValueIntermediateNode || child is CSharpCodeAttributeValueIntermediateNode || child is ExtensionIntermediateNode); context.CodeWriter .WriteStartMethodInvocation(BeginAddHtmlAttributeValuesMethodName) .Write(ExecutionContextVariableName) .WriteParameterSeparator() .WriteStringLiteral(node.AttributeName) .WriteParameterSeparator() .Write(valuePieceCount.ToString(CultureInfo.InvariantCulture)) .WriteParameterSeparator() .Write(attributeValueStyleParameter) .WriteEndMethodInvocation(); context.RenderChildren(node, new TagHelperHtmlAttributeRuntimeNodeWriter()); context.CodeWriter .WriteMethodInvocation( EndAddHtmlAttributeValuesMethodName, ExecutionContextVariableName); } else { // This is a data-* attribute which includes C#. Do not perform the conditional attribute removal or // other special cases used when IsDynamicAttributeValue(). But the attribute must still be buffered to // determine its final value. // Attribute value is not plain text, must be buffered to determine its final value. context.CodeWriter.WriteMethodInvocation(BeginWriteTagHelperAttributeMethodName); // We're building a writing scope around the provided chunks which captures everything written from the // page. Therefore, we do not want to write to any other buffer since we're using the pages buffer to // ensure we capture all content that's written, directly or indirectly. context.RenderChildren(node, new RuntimeNodeWriter()); context.CodeWriter .WriteStartAssignment(StringValueBufferVariableName) .WriteMethodInvocation(EndWriteTagHelperAttributeMethodName) .WriteStartInstanceMethodInvocation( ExecutionContextVariableName, ExecutionContextAddHtmlAttributeMethodName) .WriteStringLiteral(node.AttributeName) .WriteParameterSeparator() .WriteStartMethodInvocation(MarkAsHtmlEncodedMethodName) .Write(StringValueBufferVariableName) .WriteEndMethodInvocation(endLine: false) .WriteParameterSeparator() .Write(attributeValueStyleParameter) .WriteEndMethodInvocation(); } } }
private static void Push(CodeRenderingContext context, TagHelperIntermediateNode node) { ((DefaultCodeRenderingContext)context).AncestorsInternal.Push(node); }
private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DirectiveTokenIntermediateNode node) { var tokenKind = node.DirectiveToken.Kind; if (!node.Source.HasValue || !string.Equals( context.SourceDocument?.FilePath, node.Source.Value.FilePath, StringComparison.OrdinalIgnoreCase)) { // We don't want to handle directives from imports. return; } if (tokenKind == DirectiveTokenKind.Attribute) { // We don't need to do anything special here. // We let the Roslyn take care of providing syntax errors for C# attributes. return; } // Wrap the directive token in a lambda to isolate variable names. context.CodeWriter .Write("((") .Write(typeof(Action).FullName) .Write(")("); using (context.CodeWriter.BuildLambda()) { var originalIndent = context.CodeWriter.CurrentIndent; context.CodeWriter.CurrentIndent = 0; switch (tokenKind) { case DirectiveTokenKind.Type: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. WriteMarkerToken(context, node); break; } // {node.Content} __typeHelper = default({node.Content}); using (context.CodeWriter.BuildLinePragma(node.Source, context)) { context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .Write(" ") .WriteStartAssignment(TypeHelper) .Write("default"); if (!context.Options.SuppressNullabilityEnforcement) { context.CodeWriter.Write("!"); } context.CodeWriter.WriteLine(";"); } break; case DirectiveTokenKind.Member: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. WriteMarkerToken(context, node); break; } // global::System.Object {node.content} = null; using (context.CodeWriter.BuildLinePragma(node.Source, context)) { context.CodeWriter .Write("global::") .Write(typeof(object).FullName) .Write(" "); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .Write(" = null"); if (!context.Options.SuppressNullabilityEnforcement) { context.CodeWriter.Write("!"); } context.CodeWriter.WriteLine(";"); } break; case DirectiveTokenKind.Namespace: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. WriteMarkerToken(context, node); break; } // global::System.Object __typeHelper = nameof({node.Content}); using (context.CodeWriter.BuildLinePragma(node.Source, context)) { context.CodeWriter .Write("global::") .Write(typeof(object).FullName) .Write(" ") .WriteStartAssignment(TypeHelper); context.CodeWriter.Write("nameof("); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .WriteLine(");"); } break; case DirectiveTokenKind.String: // global::System.Object __typeHelper = "{node.Content}"; using (context.CodeWriter.BuildLinePragma(node.Source, context)) { context.CodeWriter .Write("global::") .Write(typeof(object).FullName) .Write(" ") .WriteStartAssignment(TypeHelper); if (node.Content.StartsWith("\"", StringComparison.Ordinal)) { context.AddSourceMappingFor(node); context.CodeWriter.Write(node.Content); } else { context.CodeWriter.Write("\""); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .Write("\""); } context.CodeWriter.WriteLine(";"); } break; case DirectiveTokenKind.Boolean: // global::System.Boolean __typeHelper = {node.Content}; using (context.CodeWriter.BuildLinePragma(node.Source, context)) { context.CodeWriter .Write("global::") .Write(typeof(bool).FullName) .Write(" ") .WriteStartAssignment(TypeHelper); context.AddSourceMappingFor(node); context.CodeWriter.Write(node.Content); context.CodeWriter.WriteLine(";"); } break; } context.CodeWriter.CurrentIndent = originalIndent; } context.CodeWriter.WriteLine("))();"); }
public void SetUp() { TestingCodeBox box = new TestingCodeBox(); _context = box.RenderingContext; }
private void WriteDesignTimeDirectiveToken(CodeRenderingContext context, DirectiveTokenIntermediateNode node) { var tokenKind = node.DirectiveToken.Kind; if (!node.Source.HasValue || !string.Equals( context.SourceDocument?.FilePath, node.Source.Value.FilePath, StringComparison.OrdinalIgnoreCase)) { // We don't want to handle directives from imports. return; } // Wrap the directive token in a lambda to isolate variable names. context.CodeWriter .Write("((") .Write(typeof(Action).FullName) .Write(")("); using (context.CodeWriter.BuildLambda()) { var originalIndent = context.CodeWriter.CurrentIndent; context.CodeWriter.CurrentIndent = 0; switch (tokenKind) { case DirectiveTokenKind.Type: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. WriteMarkerToken(context, node); break; } // {node.Content} __typeHelper = default({node.Content}); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .Write(" ") .WriteStartAssignment(TypeHelper) .Write("default(") .Write(node.Content) .WriteLine(");"); break; case DirectiveTokenKind.Member: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. WriteMarkerToken(context, node); break; } // global::System.Object {node.content} = null; context.CodeWriter .Write("global::") .Write(typeof(object).FullName) .Write(" "); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .WriteLine(" = null;"); break; case DirectiveTokenKind.Namespace: if (string.IsNullOrEmpty(node.Content)) { // This is most likely a marker token. WriteMarkerToken(context, node); break; } // global::System.Object __typeHelper = nameof({node.Content}); context.CodeWriter .Write("global::") .Write(typeof(object).FullName) .Write(" ") .WriteStartAssignment(TypeHelper); context.CodeWriter.Write("nameof("); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .WriteLine(");"); break; case DirectiveTokenKind.String: // global::System.Object __typeHelper = "{node.Content}"; context.CodeWriter .Write("global::") .Write(typeof(object).FullName) .Write(" ") .WriteStartAssignment(TypeHelper); if (node.Content.StartsWith("\"", StringComparison.Ordinal)) { context.AddSourceMappingFor(node); context.CodeWriter.Write(node.Content); } else { context.CodeWriter.Write("\""); context.AddSourceMappingFor(node); context.CodeWriter .Write(node.Content) .Write("\""); } context.CodeWriter.WriteLine(";"); break; } context.CodeWriter.CurrentIndent = originalIndent; } context.CodeWriter.WriteLine("))();"); }
public void WriteTemplate(CodeRenderingContext context, TemplateIntermediateNode node) { // This is OK because this will only be plugged in by the component code target // not globally. ((ComponentNodeWriter)context.NodeWriter).WriteTemplate(context, node); }
public override void WriteNode(CodeTarget target, CodeRenderingContext context) { throw new NotImplementedException(); }
public void WriteTemplate(CodeRenderingContext context, TemplateIntermediateNode node) { ((BlazorNodeWriter)context.NodeWriter).WriteTemplate(context, node); }
public override void WriteNode(CodeTarget target, CodeRenderingContext context) { context.CodeWriter.WriteLine("MyExtensionNode"); }
// Currently the same for design time and runtime public override void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (node == null) { throw new ArgumentNullException(nameof(node)); } var parameters = GetTypeInferenceMethodParameters(node); // This is really similar to the code in WriteComponentAttribute and WriteComponentChildContent - except simpler because // attributes and child contents look like variables. // // Looks like: // // public static void CreateFoo_0<T1, T2>(RenderTreeBuilder __builder, int seq, int __seq0, T1 __arg0, int __seq1, global::System.Collections.Generic.List<T2> __arg1, int __seq2, string __arg2) // { // builder.OpenComponent<Foo<T1, T2>>(); // builder.AddAttribute(__seq0, "Attr0", __arg0); // builder.AddAttribute(__seq1, "Attr1", __arg1); // builder.AddAttribute(__seq2, "Attr2", __arg2); // builder.CloseComponent(); // } // // As a special case, we need to generate a thunk for captures in this block instead of using // them verbatim. // // The problem is that RenderTreeBuilder wants an Action<object>. The caller can't write the type // name if it contains generics, and we can't write the variable they want to assign to. var writer = context.CodeWriter; writer.Write("public static void "); writer.Write(node.MethodName); writer.Write("<"); writer.Write(string.Join(", ", node.Component.Component.GetTypeParameters().Select(a => a.Name))); writer.Write(">"); writer.Write("("); writer.Write("global::"); writer.Write(ComponentsApi.RenderTreeBuilder.FullTypeName); writer.Write(" "); writer.Write(ComponentsApi.RenderTreeBuilder.BuilderParameter); writer.Write(", "); writer.Write("int seq"); if (parameters.Count > 0) { writer.Write(", "); } for (var i = 0; i < parameters.Count; i++) { if (!string.IsNullOrEmpty(parameters[i].SeqName)) { writer.Write("int "); writer.Write(parameters[i].SeqName); writer.Write(", "); } writer.Write(parameters[i].TypeName); writer.Write(" "); writer.Write(parameters[i].ParameterName); if (i < parameters.Count - 1) { writer.Write(", "); } } writer.Write(")"); writer.WriteLine(); writer.WriteLine("{"); // _builder.OpenComponent<TComponent>(42); context.CodeWriter.Write(ComponentsApi.RenderTreeBuilder.BuilderParameter); context.CodeWriter.Write("."); context.CodeWriter.Write(ComponentsApi.RenderTreeBuilder.OpenComponent); context.CodeWriter.Write("<"); context.CodeWriter.Write(node.Component.TypeName); context.CodeWriter.Write(">("); context.CodeWriter.Write("seq"); context.CodeWriter.Write(");"); context.CodeWriter.WriteLine(); foreach (var parameter in parameters) { switch (parameter.Source) { case ComponentAttributeIntermediateNode attribute: context.CodeWriter.WriteStartInstanceMethodInvocation(ComponentsApi.RenderTreeBuilder.BuilderParameter, ComponentsApi.RenderTreeBuilder.AddAttribute); context.CodeWriter.Write(parameter.SeqName); context.CodeWriter.Write(", "); context.CodeWriter.Write($"\"{attribute.AttributeName}\""); context.CodeWriter.Write(", "); context.CodeWriter.Write(parameter.ParameterName); context.CodeWriter.WriteEndMethodInvocation(); break; case SplatIntermediateNode: context.CodeWriter.WriteStartInstanceMethodInvocation(ComponentsApi.RenderTreeBuilder.BuilderParameter, ComponentsApi.RenderTreeBuilder.AddMultipleAttributes); context.CodeWriter.Write(parameter.SeqName); context.CodeWriter.Write(", "); context.CodeWriter.Write(parameter.ParameterName); context.CodeWriter.WriteEndMethodInvocation(); break; case ComponentChildContentIntermediateNode childContent: context.CodeWriter.WriteStartInstanceMethodInvocation(ComponentsApi.RenderTreeBuilder.BuilderParameter, ComponentsApi.RenderTreeBuilder.AddAttribute); context.CodeWriter.Write(parameter.SeqName); context.CodeWriter.Write(", "); context.CodeWriter.Write($"\"{childContent.AttributeName}\""); context.CodeWriter.Write(", "); context.CodeWriter.Write(parameter.ParameterName); context.CodeWriter.WriteEndMethodInvocation(); break; case SetKeyIntermediateNode: context.CodeWriter.WriteStartInstanceMethodInvocation(ComponentsApi.RenderTreeBuilder.BuilderParameter, ComponentsApi.RenderTreeBuilder.SetKey); context.CodeWriter.Write(parameter.ParameterName); context.CodeWriter.WriteEndMethodInvocation(); break; case ReferenceCaptureIntermediateNode capture: context.CodeWriter.WriteStartInstanceMethodInvocation(ComponentsApi.RenderTreeBuilder.BuilderParameter, capture.IsComponentCapture ? ComponentsApi.RenderTreeBuilder.AddComponentReferenceCapture : ComponentsApi.RenderTreeBuilder.AddElementReferenceCapture); context.CodeWriter.Write(parameter.SeqName); context.CodeWriter.Write(", "); var cast = capture.IsComponentCapture ? $"({capture.ComponentCaptureTypeName})" : string.Empty; context.CodeWriter.Write($"(__value) => {{ {parameter.ParameterName}({cast}__value); }}"); context.CodeWriter.WriteEndMethodInvocation(); break; case CascadingGenericTypeParameter: // We only use the synthetic cascading parameters for type inference break; default: throw new InvalidOperationException($"Not implemented: type inference method parameter from source {parameter.Source}"); } } context.CodeWriter.WriteInstanceMethodInvocation(ComponentsApi.RenderTreeBuilder.BuilderParameter, ComponentsApi.RenderTreeBuilder.CloseComponent); writer.WriteLine("}"); if (node.Component.Component.SuppliesCascadingGenericParameters()) { // If this component cascades any generic parameters, we'll need to be able to capture its type inference // args at the call site. The point of this is to ensure that: // // [1] We only evaluate each expression once // [2] We evaluate them in the correct order matching the developer's source // [3] We can even make variables for lambdas or other expressions that can't just be assigned to implicitly-typed vars. // // We do that by emitting a method like the following. It has exactly the same generic type inference // characteristics as the regular CreateFoo_0 method emitted earlier // // public static void CreateFoo_0_CaptureParameters<T1, T2>(T1 __arg0, out T1 __arg0_out, global::System.Collections.Generic.List<T2> __arg1, out global::System.Collections.Generic.List<T2> __arg1_out, int __seq2, string __arg2, out string __arg2_out) // { // __arg0_out = __arg0; // __arg1_out = __arg1; // __arg2_out = __arg2; // } // writer.WriteLine(); writer.Write("public static void "); writer.Write(node.MethodName); writer.Write("_CaptureParameters<"); writer.Write(string.Join(", ", node.Component.Component.GetTypeParameters().Select(a => a.Name))); writer.Write(">"); writer.Write("("); var isFirst = true; foreach (var parameter in parameters.Where(p => p.UsedForTypeInference)) { if (isFirst) { isFirst = false; } else { writer.Write(", "); } writer.Write(parameter.TypeName); writer.Write(" "); writer.Write(parameter.ParameterName); writer.Write(", out "); writer.Write(parameter.TypeName); writer.Write(" "); writer.Write(parameter.ParameterName); writer.Write("_out"); } writer.WriteLine(")"); writer.WriteLine("{"); foreach (var parameter in parameters.Where(p => p.UsedForTypeInference)) { writer.Write(" "); writer.Write(parameter.ParameterName); writer.Write("_out = "); writer.Write(parameter.ParameterName); writer.WriteLine(";"); } writer.WriteLine("}"); } }
public override void WriteNode(CodeTarget target, CodeRenderingContext context) => context.CodeWriter.WriteAutoPropertyDeclaration( _injectedPropertyModifiers, TypeName, MemberName);
public static CodeWriter WritePadding(this CodeWriter writer, int offset, SourceSpan?span, CodeRenderingContext context) { if (span == null) { return(writer); } var basePadding = CalculatePadding(); var resolvedPadding = Math.Max(basePadding - offset, 0); if (context.Options.IndentWithTabs) { // Avoid writing directly to the StringBuilder here, that will throw off the manual indexing // done by the base class. var tabs = resolvedPadding / context.Options.IndentSize; for (var i = 0; i < tabs; i++) { writer.Write("\t"); } var spaces = resolvedPadding % context.Options.IndentSize; for (var i = 0; i < spaces; i++) { writer.Write(" "); } } else { for (var i = 0; i < resolvedPadding; i++) { writer.Write(" "); } } return(writer); int CalculatePadding() { var spaceCount = 0; for (var i = span.Value.AbsoluteIndex - 1; i >= 0; i--) { var @char = context.SourceDocument[i]; if (@char == '\n' || @char == '\r') { break; } else if (@char == '\t') { spaceCount += context.Options.IndentSize; } else { spaceCount++; } } return(spaceCount); } }
public abstract void WriteComponentTypeArgument(CodeRenderingContext context, ComponentTypeArgumentExtensionNode node);
public void WriteTagHelperProperty(CodeRenderingContext context, DefaultTagHelperPropertyIntermediateNode node) { var tagHelperNode = context.Parent as TagHelperIntermediateNode; if (context.Parent == null) { var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode)); throw new InvalidOperationException(message); } if (!context.Options.DesignTime) { // Ensure that the property we're trying to set has initialized its dictionary bound properties. if (node.IsIndexerNameMatch && object.ReferenceEquals(FindFirstUseOfIndexer(tagHelperNode, node), node)) { // Throw a reasonable Exception at runtime if the dictionary property is null. context.CodeWriter .Write("if (") .Write(node.FieldName) .Write(".") .Write(node.PropertyName) .WriteLine(" == null)"); using (context.CodeWriter.BuildScope()) { // System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName // of InvalidOperationException type. context.CodeWriter .Write("throw ") .WriteStartNewObject(nameof(InvalidOperationException)) .WriteStartMethodInvocation(FormatInvalidIndexerAssignmentMethodName) .WriteStringLiteral(node.AttributeName) .WriteParameterSeparator() .WriteStringLiteral(node.TagHelper.GetTypeName()) .WriteParameterSeparator() .WriteStringLiteral(node.PropertyName) .WriteEndMethodInvocation(endLine: false) // End of method call .WriteEndMethodInvocation(); // End of new expression / throw statement } } } // If this is not the first use of the attribute value, we need to evaluate the expression and assign it to // the tag helper property. // // Otherwise, the value has already been computed and assigned to another tag helper. We just need to // copy from that tag helper to this one. // // This is important because we can't evaluate the expression twice due to side-effects. var firstUseOfAttribute = FindFirstUseOfAttribute(tagHelperNode, node); if (!object.ReferenceEquals(firstUseOfAttribute, node)) { // If we get here, this value has already been used. We just need to copy the value. context.CodeWriter .WriteStartAssignment(GetPropertyAccessor(node)) .Write(GetPropertyAccessor(firstUseOfAttribute)) .WriteLine(";"); return; } // If we get there, this is the first time seeing this property so we need to evaluate the expression. if (node.BoundAttribute.ExpectsStringValue(node.AttributeName)) { if (context.Options.DesignTime) { context.RenderChildren(node); context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node)); if (node.Children.Count == 1 && node.Children.First() is HtmlContentIntermediateNode htmlNode) { var content = GetContent(htmlNode); context.CodeWriter.WriteStringLiteral(content); } else { context.CodeWriter.Write("string.Empty"); } context.CodeWriter.WriteLine(";"); } else { context.CodeWriter.WriteMethodInvocation(BeginWriteTagHelperAttributeMethodName); context.RenderChildren(node, new LiteralRuntimeNodeWriter()); context.CodeWriter .WriteStartAssignment(StringValueBufferVariableName) .WriteMethodInvocation(EndWriteTagHelperAttributeMethodName) .WriteStartAssignment(GetPropertyAccessor(node)) .Write(StringValueBufferVariableName) .WriteLine(";"); } } else { if (context.Options.DesignTime) { var firstMappedChild = node.Children.FirstOrDefault(child => child.Source != null) as IntermediateNode; var valueStart = firstMappedChild?.Source; using (context.CodeWriter.BuildLinePragma(node.Source, context)) { var accessor = GetPropertyAccessor(node); var assignmentPrefixLength = accessor.Length + " = ".Length; if (node.BoundAttribute.IsEnum && node.Children.Count == 1 && node.Children.First() is IntermediateToken token && token.IsCSharp) { assignmentPrefixLength += $"global::{node.BoundAttribute.TypeName}.".Length; if (valueStart != null) { context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context); } context.CodeWriter .WriteStartAssignment(accessor) .Write("global::") .Write(node.BoundAttribute.TypeName) .Write("."); } else { if (valueStart != null) { context.CodeWriter.WritePadding(assignmentPrefixLength, node.Source, context); } context.CodeWriter.WriteStartAssignment(GetPropertyAccessor(node)); } if (node.Children.Count == 0 && node.AttributeStructure == AttributeStructure.Minimized && node.BoundAttribute.ExpectsBooleanValue(node.AttributeName)) { // If this is a minimized boolean attribute, set the value to true. context.CodeWriter.Write("true"); } else { RenderTagHelperAttributeInline(context, node, node.Source); } context.CodeWriter.WriteLine(";"); } }
// Currently the same for design time and runtime public void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (node == null) { throw new ArgumentNullException(nameof(node)); } // This is ugly because CodeWriter doesn't allow us to erase, but we need to comma-delimit. So we have to // materizalize something can iterate, or use string.Join. We'll need this multiple times, so materializing // it. var parameters = GetParameterDeclarations(); // This is really similar to the code in WriteComponentAttribute and WriteComponentChildContent - except simpler because // attributes and child contents look like variables. // // Looks like: // // public static void CreateFoo_0<T1, T2>(RenderTreeBuilder builder, int seq, int __seq0, T1 __arg0, int __seq1, global::System.Collections.Generic.List<T2> __arg1, int __seq2, string __arg2) // { // builder.OpenComponent<Foo<T1, T2>>(); // builder.AddAttribute(__seq0, "Attr0", __arg0); // builder.AddAttribute(__seq1, "Attr1", __arg1); // builder.AddAttribute(__seq2, "Attr2", __arg2); // builder.CloseComponent(); // } // // As a special case, we need to generate a thunk for captures in this block instead of using // them verbatim. // // The problem is that RenderTreeBuilder wants an Action<object>. The caller can't write the type // name if it contains generics, and we can't write the variable they want to assign to. var writer = context.CodeWriter; writer.Write("public static void "); writer.Write(node.MethodName); writer.Write("<"); writer.Write(string.Join(", ", node.Component.Component.GetTypeParameters().Select(a => a.Name))); writer.Write(">"); writer.Write("("); writer.Write("global::"); writer.Write(ComponentsApi.RenderTreeBuilder.FullTypeName); writer.Write(" builder"); writer.Write(", "); writer.Write("int seq"); if (parameters.Count > 0) { writer.Write(", "); } for (var i = 0; i < parameters.Count; i++) { writer.Write("int "); writer.Write(parameters[i].seqName); writer.Write(", "); writer.Write(parameters[i].typeName); writer.Write(" "); writer.Write(parameters[i].parameterName); if (i < parameters.Count - 1) { writer.Write(", "); } } writer.Write(")"); writer.WriteLine(); writer.WriteLine("{"); // builder.OpenComponent<TComponent>(42); context.CodeWriter.Write("builder"); context.CodeWriter.Write("."); context.CodeWriter.Write(ComponentsApi.RenderTreeBuilder.OpenComponent); context.CodeWriter.Write("<"); context.CodeWriter.Write(node.Component.TypeName); context.CodeWriter.Write(">("); context.CodeWriter.Write("seq"); context.CodeWriter.Write(");"); context.CodeWriter.WriteLine(); var index = 0; foreach (var attribute in node.Component.Attributes) { context.CodeWriter.WriteStartInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.AddAttribute); context.CodeWriter.Write(parameters[index].seqName); context.CodeWriter.Write(", "); context.CodeWriter.Write($"\"{attribute.AttributeName}\""); context.CodeWriter.Write(", "); context.CodeWriter.Write(parameters[index].parameterName); context.CodeWriter.WriteEndMethodInvocation(); index++; } foreach (var childContent in node.Component.ChildContents) { context.CodeWriter.WriteStartInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.AddAttribute); context.CodeWriter.Write(parameters[index].seqName); context.CodeWriter.Write(", "); context.CodeWriter.Write($"\"{childContent.AttributeName}\""); context.CodeWriter.Write(", "); context.CodeWriter.Write(parameters[index].parameterName); context.CodeWriter.WriteEndMethodInvocation(); index++; } foreach (var capture in node.Component.Captures) { context.CodeWriter.WriteStartInstanceMethodInvocation("builder", capture.IsComponentCapture ? ComponentsApi.RenderTreeBuilder.AddComponentReferenceCapture : ComponentsApi.RenderTreeBuilder.AddElementReferenceCapture); context.CodeWriter.Write(parameters[index].seqName); context.CodeWriter.Write(", "); var cast = capture.IsComponentCapture ? $"({capture.ComponentCaptureTypeName})" : string.Empty; context.CodeWriter.Write($"(__value) => {{ {parameters[index].parameterName}({cast}__value); }}"); context.CodeWriter.WriteEndMethodInvocation(); index++; } context.CodeWriter.WriteInstanceMethodInvocation("builder", ComponentsApi.RenderTreeBuilder.CloseComponent); writer.WriteLine("}"); List <(string seqName, string typeName, string parameterName)> GetParameterDeclarations() { var p = new List <(string seqName, string typeName, string parameterName)>(); foreach (var attribute in node.Component.Attributes) { p.Add(($"__seq{p.Count}", attribute.TypeName, $"__arg{p.Count}")); } foreach (var childContent in node.Component.ChildContents) { p.Add(($"__seq{p.Count}", childContent.TypeName, $"__arg{p.Count}")); } foreach (var capture in node.Component.Captures) { p.Add(($"__seq{p.Count}", capture.TypeName, $"__arg{p.Count}")); } return(p); } }
public abstract void WriteComponentAttribute(CodeRenderingContext context, ComponentAttributeExtensionNode node);
public void DrawToGraphics(FormattedCode code, CodeRenderingContext args, Rectangle viewport) { CURRENTLINE_INDEX = args.CurrentLine; }
public abstract void WriteHtmlElement(CodeRenderingContext context, HtmlElementIntermediateNode node);
protected abstract void BeginWriteAttribute(CodeRenderingContext context, string key);
public abstract void WriteReferenceCapture(CodeRenderingContext context, RefExtensionNode node);
protected abstract void BeginWriteAttribute(CodeRenderingContext context, IntermediateNode expression);
public sealed override void BeginWriterScope(CodeRenderingContext context, string writer) { throw new NotImplementedException(nameof(BeginWriterScope)); }
protected abstract void WriteReferenceCaptureInnards(CodeRenderingContext context, ReferenceCaptureIntermediateNode node, bool shouldTypeCheck);
public abstract void WriteNode(CodeTarget target, CodeRenderingContext context);
public abstract void BeginWriterScope(CodeRenderingContext context, string writer);