private ComponentIntermediateNode RewriteAsComponent(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper) { var component = new ComponentIntermediateNode() { Component = tagHelper, Source = node.Source, TagName = node.TagName, TypeName = tagHelper.GetTypeName(), }; for (var i = 0; i < node.Diagnostics.Count; i++) { component.Diagnostics.Add(node.Diagnostics[i]); } var visitor = new ComponentRewriteVisitor(component); visitor.Visit(node); // Fixup the parameter names of child content elements. We can't do this during the rewrite // because we see the nodes in the wrong order. foreach (var childContent in component.ChildContents) { childContent.ParameterName = childContent.ParameterName ?? component.ChildContentParameterName ?? ComponentMetadata.ChildContent.DefaultParameterName; } return(component); }
private void AddField(Context context, TagHelperDescriptor tagHelper) { // We need to insert a node for the field that will hold the tag helper. We've already generated a field name // at this time and use it for all uses of the same tag helper type. // // We also want to preserve the ordering of the nodes for testability. So insert at the end of any existing // field nodes. var i = 0; while (i < context.Class.Children.Count && context.Class.Children[i] is DefaultTagHelperRuntimeIntermediateNode) { i++; } while (i < context.Class.Children.Count && context.Class.Children[i] is FieldDeclarationIntermediateNode) { i++; } context.Class.Children.Insert(i, new FieldDeclarationIntermediateNode() { Annotations = { { CommonAnnotations.DefaultTagHelperExtension.TagHelperField, bool.TrueString }, }, Modifiers = { "private", }, FieldName = context.GetFieldName(tagHelper), FieldType = "global::" + tagHelper.GetTypeName(), }); }
private static TagHelperDescriptor FindAssociatedTagHelper(TagHelperDescriptor tagHelper, IReadOnlyList <TagHelperDescriptor> tagHelpers) { var typeName = tagHelper.GetTypeName(); var assemblyName = tagHelper.AssemblyName; for (var i = 0; i < tagHelpers.Count; i++) { var currentTagHelper = tagHelpers[i]; if (tagHelper == currentTagHelper) { // Same as the primary, we're looking for our other pair. continue; } var currentTypeName = currentTagHelper.GetTypeName(); if (!string.Equals(typeName, currentTypeName, StringComparison.Ordinal)) { continue; } if (!string.Equals(assemblyName, currentTagHelper.AssemblyName, StringComparison.Ordinal)) { continue; } // Found our associated TagHelper, there should only ever be 1 other associated TagHelper (fully qualified and non-fully qualified). return(currentTagHelper); } return(null); }
public static BoundElementDescriptionInfo From(TagHelperDescriptor tagHelper) { var tagHelperTypeName = tagHelper.GetTypeName(); var descriptionInfo = new BoundElementDescriptionInfo(tagHelperTypeName, tagHelper.Documentation); return(descriptionInfo); }
private void RewriteUsage(Context context, TagHelperIntermediateNode node, TagHelperDescriptor tagHelper) { if (!tagHelper.IsDefaultKind()) { return; } context.Add(tagHelper); // First we need to insert a node for the creation of the tag helper, and the hook up to the execution // context. This should come after the body node and any existing create nodes. // // If we're dealing with something totally malformed, then we'll end up just inserting at the end, and that's not // so bad. var i = 0; // Find the body node. while (i < node.Children.Count && node.Children[i] is TagHelperBodyIntermediateNode) { i++; } while (i < node.Children.Count && node.Children[i] is DefaultTagHelperBodyIntermediateNode) { i++; } // Now find the last create node. while (i < node.Children.Count && node.Children[i] is DefaultTagHelperCreateIntermediateNode) { i++; } // Now i has the right insertion point. node.Children.Insert(i, new DefaultTagHelperCreateIntermediateNode() { FieldName = context.GetFieldName(tagHelper), TagHelper = tagHelper, TypeName = tagHelper.GetTypeName(), }); // Next we need to rewrite any property nodes to use the field and property name for this // tag helper. for (i = 0; i < node.Children.Count; i++) { if (node.Children[i] is TagHelperPropertyIntermediateNode propertyNode && propertyNode.TagHelper == tagHelper) { // This belongs to the current tag helper, replace it. node.Children[i] = new DefaultTagHelperPropertyIntermediateNode(propertyNode) { FieldName = context.GetFieldName(tagHelper), PropertyName = propertyNode.BoundAttribute.GetPropertyName(), }; } } }
private void RewriteUsage(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper) { // Ignore Kind here. Some versions of Razor have a bug in the serializer that ignores it. // We need to surround the contents of the node with open and close nodes to ensure the component // is scoped correctly. node.Children.Insert(0, new ComponentOpenExtensionNode() { TypeName = tagHelper.GetTypeName(), }); for (var i = node.Children.Count - 1; i >= 0; i--) { if (node.Children[i] is TagHelperBodyIntermediateNode bodyNode) { // Replace with a node that we recognize so that it we can do proper scope tracking. // // Note that we force the body node to be last, this is done to push it after the // attribute nodes. This gives us the ordering we want for the render tree. node.Children.RemoveAt(i); node.Children.Add(new ComponentBodyExtensionNode(bodyNode) { TagMode = node.TagMode, TagName = node.TagName, }); } } node.Children.Add(new ComponentCloseExtensionNode()); // Now we need to rewrite any set property or HTML nodes to call the appropriate AddAttribute api. for (var i = node.Children.Count - 1; i >= 0; i--) { if (node.Children[i] is TagHelperPropertyIntermediateNode propertyNode && propertyNode.TagHelper == tagHelper) { // We don't support 'complex' content for components (mixed C# and markup) right now. // It's not clear yet if Blazor will have a good scenario to use these constructs. // // This is where a lot of the complexity in the Razor/TagHelpers model creeps in and we // might be able to avoid it if these features aren't needed. if (HasComplexChildContent(propertyNode)) { node.Diagnostics.Add(BlazorDiagnosticFactory.Create_UnsupportedComplexContent( propertyNode, propertyNode.AttributeName)); node.Children.RemoveAt(i); continue; } node.Children[i] = new ComponentAttributeExtensionNode(propertyNode); }
internal static bool TrySplitNamespaceAndType(TagHelperDescriptor tagHelperDescriptor, out StringSegment @namespace, out StringSegment typeName) { if (tagHelperDescriptor.ParsedTypeInfo is { } value) { @namespace = value.Namespace; typeName = value.TypeName; return(value.Success); } var success = TrySplitNamespaceAndType(tagHelperDescriptor.GetTypeName(), out @namespace, out typeName); tagHelperDescriptor.ParsedTypeInfo = new(success, @namespace, typeName); return(success); }
private TagHelperDescriptor CreateChildContentDescriptor(ComponentSymbols symbols, TagHelperDescriptor component, BoundAttributeDescriptor attribute) { var typeName = component.GetTypeName() + "." + attribute.Name; var assemblyName = component.AssemblyName; var builder = TagHelperDescriptorBuilder.Create(ComponentMetadata.ChildContent.TagHelperKind, typeName, assemblyName); builder.SetTypeName(typeName); builder.CaseSensitive = true; // This opts out this 'component' tag helper for any processing that's specific to the default // Razor ITagHelper runtime. builder.Metadata[TagHelperMetadata.Runtime.Name] = ComponentMetadata.ChildContent.RuntimeName; // Opt out of processing as a component. We'll process this specially as part of the component's body. builder.Metadata[ComponentMetadata.SpecialKindKey] = ComponentMetadata.ChildContent.TagHelperKind; var xml = attribute.Documentation; if (!string.IsNullOrEmpty(xml)) { builder.Documentation = xml; } // Child content matches the property name, but only as a direct child of the component. builder.TagMatchingRule(r => { r.TagName = attribute.Name; r.ParentTag = component.TagMatchingRules.First().TagName; }); if (attribute.IsParameterizedChildContentProperty()) { // For child content attributes with a parameter, synthesize an attribute that allows you to name // the parameter. CreateContextParameter(builder, attribute.Name); } if (component.IsComponentFullyQualifiedNameMatch()) { builder.Metadata[ComponentMetadata.Component.NameMatchKey] = ComponentMetadata.Component.FullyQualifiedNameMatch; } var descriptor = builder.Build(); return(descriptor); }
private ComponentExtensionNode RewriteAsComponent(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper) { var component = new ComponentExtensionNode() { Component = tagHelper, Source = node.Source, TagName = node.TagName, TypeName = tagHelper.GetTypeName(), }; for (var i = 0; i < node.Diagnostics.Count; i++) { component.Diagnostics.Add(node.Diagnostics[i]); } var visitor = new ComponentRewriteVisitor(component); visitor.Visit(node); return(component); }
private void RewriteUsage(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper) { // We need to surround the contents of the node with open and close nodes to ensure the component // is scoped correctly. node.Children.Insert(0, new ComponentOpenExtensionNode() { TypeName = tagHelper.GetTypeName(), }); for (var i = node.Children.Count - 1; i >= 0; i--) { if (node.Children[i] is TagHelperBodyIntermediateNode bodyNode) { // Replace with a node that we recognize so that it we can do proper scope tracking. // // Note that we force the body node to be last, this is done to push it after the // attribute nodes. This gives us the ordering we want for the render tree. node.Children.RemoveAt(i); node.Children.Add(new ComponentBodyExtensionNode(bodyNode) { TagMode = node.TagMode, TagName = node.TagName, }); } } node.Children.Add(new ComponentCloseExtensionNode()); // Now we need to rewrite any set property or HTML nodes to call the appropriate AddAttribute api. for (var i = node.Children.Count - 1; i >= 0; i--) { if (node.Children[i] is TagHelperPropertyIntermediateNode propertyNode && propertyNode.TagHelper == tagHelper) { node.Children[i] = new ComponentAttributeExtensionNode(propertyNode); }
private static string GenerateFieldName(TagHelperDescriptor tagHelper) { return("__" + tagHelper.GetTypeName().Replace('.', '_')); }