protected override void Visit(TagHelperChunk chunk) { // We only want to setup tag helper manager fields if there are tag helpers, and only once if (!_foundTagHelpers) { _foundTagHelpers = true; // We want to hide declared TagHelper fields so they cannot be stepped over via a debugger. Writer.WriteLineHiddenDirective(); // Runtime fields aren't useful during design time. if (!Context.Host.DesignTimeMode) { // Need to disable the warning "X is assigned to but never used." for the value buffer since // whether it's used depends on how a TagHelper is used. Writer.WritePragma("warning disable 0414"); WritePrivateField( _tagHelperContext.TagHelperContentTypeName, CSharpTagHelperCodeRenderer.StringValueBufferVariableName, value: null); Writer.WritePragma("warning restore 0414"); WritePrivateField( _tagHelperContext.ExecutionContextTypeName, CSharpTagHelperCodeRenderer.ExecutionContextVariableName, value: null); WritePrivateField( _tagHelperContext.RunnerTypeName, CSharpTagHelperCodeRenderer.RunnerVariableName, value: null); WritePrivateField( _tagHelperContext.ScopeManagerTypeName, CSharpTagHelperCodeRenderer.ScopeManagerVariableName, value: null); } } foreach (var descriptor in chunk.Descriptors) { if (!_declaredTagHelpers.Contains(descriptor.TypeName)) { _declaredTagHelpers.Add(descriptor.TypeName); WritePrivateField( descriptor.TypeName, CSharpTagHelperCodeRenderer.GetVariableName(descriptor), value: null); } } if (!Context.Host.DesignTimeMode) { PreAllocateUnboundTagHelperAttributes(chunk); } // We need to dive deeper to ensure we pick up any nested tag helpers. Accept(chunk.Children); }
/// <summary> /// Renders the code for the given <paramref name="chunk"/>. /// </summary> /// <param name="chunk">A <see cref="TagHelperChunk"/> to render.</param> public void RenderTagHelper(TagHelperChunk chunk) { // Remove any duplicate TagHelperDescriptors that reference the same type name. Duplicates can occur when // multiple TargetElement attributes are on a TagHelper type and matchs overlap for an HTML element. // Having more than one descriptor with the same TagHelper type results in generated code that runs // the same TagHelper X many times (instead of once) over a single HTML element. var tagHelperDescriptors = chunk.Descriptors.Distinct(TypeNameTagHelperDescriptorComparer.Default); RenderBeginTagHelperScope(chunk.TagName, chunk.SelfClosing, chunk.Children); RenderTagHelpersCreation(chunk, tagHelperDescriptors); var attributeDescriptors = tagHelperDescriptors.SelectMany(descriptor => descriptor.Attributes); var boundHTMLAttributes = attributeDescriptors.Select(descriptor => descriptor.Name); var htmlAttributes = chunk.Attributes; var unboundHTMLAttributes = htmlAttributes.Where(htmlAttribute => !boundHTMLAttributes.Contains(htmlAttribute.Key, StringComparer.OrdinalIgnoreCase)); RenderUnboundHTMLAttributes(unboundHTMLAttributes); // No need to run anything in design time mode. if (!_designTimeMode) { RenderRunTagHelpers(); RenderWriteTagHelperMethodCall(); RenderEndTagHelpersScope(); } }
/// <summary> /// Renders the code for the given <paramref name="chunk"/>. /// </summary> /// <param name="chunk">A <see cref="TagHelperChunk"/> to render.</param> public void RenderTagHelper(TagHelperChunk chunk) { var tagHelperDescriptors = chunk.Descriptors; RenderBeginTagHelperScope(chunk.TagName, chunk.Children); RenderTagHelpersCreation(chunk); var attributeDescriptors = tagHelperDescriptors.SelectMany(descriptor => descriptor.Attributes); var boundHTMLAttributes = attributeDescriptors.Select(descriptor => descriptor.Name); var htmlAttributes = chunk.Attributes; var unboundHTMLAttributes = htmlAttributes.Where(htmlAttribute => !boundHTMLAttributes.Contains(htmlAttribute.Key, StringComparer.OrdinalIgnoreCase)); RenderUnboundHTMLAttributes(unboundHTMLAttributes); RenderRunTagHelpers(); RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); RenderTagOutput(_tagHelperContext.OutputGeneratePreContentMethodName); RenderTagHelperContent(); RenderTagOutput(_tagHelperContext.OutputGeneratePostContentMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); RenderEndTagHelpersScope(); }
private void RenderTagHelpersCreation(TagHelperChunk chunk) { var tagHelperDescriptors = chunk.Descriptors; // This is to maintain value accessors for attributes when creating the TagHelpers. // Ultimately it enables us to do scenarios like this: // myTagHelper1.Foo = DateTime.Now; // myTagHelper2.Foo = myTagHelper1.Foo; var htmlAttributeValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var tagHelperDescriptor in tagHelperDescriptors) { var tagHelperVariableName = GetVariableName(tagHelperDescriptor); // Create the tag helper _writer.WriteStartAssignment(tagHelperVariableName) .WriteStartMethodInvocation(_tagHelperContext.CreateTagHelperMethodName, tagHelperDescriptor.TypeName) .WriteEndMethodInvocation(); // Execution contexts are a runtime feature. if (!_designTimeMode) { _writer.WriteInstanceMethodInvocation(ExecutionContextVariableName, _tagHelperContext.ExecutionContextAddMethodName, tagHelperVariableName); } // Render all of the bound attribute values for the tag helper. RenderBoundHTMLAttributes(chunk.Attributes, tagHelperVariableName, tagHelperDescriptor.Attributes, htmlAttributeValues); } }
protected override void Visit(TagHelperChunk chunk) { if (chunk.TagName == "a") { Writer.Write(@"WriteLiteral("" "");"); TagHelperRenderer.RenderTagHelper(chunk); _comingOffHyperlink = true; } else { TagHelperRenderer.RenderTagHelper(chunk); } }
protected override void Visit(TagHelperChunk chunk) { if (!_foundTagHelpers) { _foundTagHelpers = true; if (!ImportedUsings.Contains(TagHelpersRuntimeNamespace)) { // If we find TagHelpers then we need to add the TagHelper runtime namespace to our list of usings. Writer.WriteUsing(TagHelpersRuntimeNamespace); ImportedUsings.Add(TagHelpersRuntimeNamespace); } } }
/// <summary> /// Writes the TagHelperRunner initialization code to the Writer. /// </summary> /// <param name="chunk">The <see cref="TagHelperChunk"/>.</param> protected override void Visit(TagHelperChunk chunk) { if (!_foundTagHelpers && !Context.Host.DesignTimeMode) { _foundTagHelpers = true; Writer .WriteStartAssignment(CSharpTagHelperCodeRenderer.RunnerVariableName) .Write(CSharpTagHelperCodeRenderer.RunnerVariableName) .Write(" ?? ") .WriteStartNewObject(_tagHelperContext.RunnerTypeName) .WriteEndMethodInvocation(); } }
private static TagHelperChunk GetViewComponentTagHelperChunk(string name, bool visitedTagHelperChunks) { var typeName = visitedTagHelperChunks ? $"{_testNamespace}.{_testClass}.{name}Type" : $"{name}Type"; var attribute = new TagHelperAttributeDescriptor { Name = "attribute", PropertyName = "Attribute", TypeName = typeof(string).FullName }; var requiredAttribute = new TagHelperRequiredAttributeDescriptor { Name = "Attribute" }; var tagHelperDescriptor = new TagHelperDescriptor { AssemblyName = $"{name}Assembly", TagName = name.ToLowerInvariant(), TypeName = typeName, Attributes = new[] { attribute }, RequiredAttributes = new[] { requiredAttribute } }; tagHelperDescriptor.PropertyBag.Add( ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, name); var tagHelperChunk = new TagHelperChunk( $"vc:{name.ToLowerInvariant()}", TagMode.SelfClosing, new List <TagHelperAttributeTracker>(), new[] { tagHelperDescriptor }); return(tagHelperChunk); }
protected override void Visit(TagHelperChunk chunk) { using (_context.Builder.BuildBlock <RenderTagHelper>(renderTagHelper => { renderTagHelper.DocumentLocation = CreateMappingLocation(chunk.Start, chunk.Association.Length); })) { AddTagHelperStructure(chunk.TagName, chunk.TagMode, chunk.Children); var descriptors = chunk.Descriptors.Distinct(TypeBasedTagHelperDescriptorComparer.Default); AddTagHelperCreation(descriptors); AddTagHelperAttributes(chunk.Attributes, descriptors); AddExecuteTagHelpers(); } }
protected override void Visit(TagHelperChunk chunk) { // We only want to setup tag helper manager fields if there are tag helpers, and only once if (!_foundTagHelpers) { _foundTagHelpers = true; // We want to hide declared TagHelper fields so they cannot be stepped over via a debugger. Writer.WriteLineHiddenDirective(); // Runtime fields aren't useful during design time. if (!Context.Host.DesignTimeMode) { WritePrivateField(typeof(TextWriter).FullName, CSharpTagHelperCodeRenderer.StringValueBufferVariableName, value: null); WritePrivateField(_tagHelperContext.ExecutionContextTypeName, CSharpTagHelperCodeRenderer.ExecutionContextVariableName, value: null); WritePrivateField(_tagHelperContext.RunnerTypeName, CSharpTagHelperCodeRenderer.RunnerVariableName, "new " + _tagHelperContext.RunnerTypeName + "()"); WritePrivateField(_tagHelperContext.ScopeManagerTypeName, CSharpTagHelperCodeRenderer.ScopeManagerVariableName, "new " + _tagHelperContext.ScopeManagerTypeName + "()"); } } foreach (var descriptor in chunk.Descriptors) { if (!_declaredTagHelpers.Contains(descriptor.TypeName)) { _declaredTagHelpers.Add(descriptor.TypeName); WritePrivateField(descriptor.TypeName, CSharpTagHelperCodeRenderer.GetVariableName(descriptor), value: null); } } // We need to dive deeper to ensure we pick up any nested tag helpers. Accept(chunk.Children); }
private static TagHelperChunk GetTagHelperChunk(string name) { var tagHelperChunk = new TagHelperChunk( name.ToLowerInvariant(), TagMode.SelfClosing, new List <TagHelperAttributeTracker>(), new List <TagHelperDescriptor> { new TagHelperDescriptor { AssemblyName = $"{name}Assembly", TagName = name.ToLowerInvariant(), TypeName = $"{name}Type", } }); return(tagHelperChunk); }
protected override void Visit(TagHelperChunk chunk) { foreach (var descriptor in chunk.Descriptors) { string shortName; if (descriptor.PropertyBag.TryGetValue( ViewComponentTagHelperDescriptorConventions.ViewComponentNameKey, out shortName)) { var typeName = $"__Generated__{shortName}ViewComponentTagHelper"; if (_writtenViewComponents.Add(typeName)) { WriteClass(descriptor); } } } }
private void RenderTagHelperOutput(TagHelperChunk chunk) { var tagHelperOutputAccessor = $"{ExecutionContextVariableName}.{_tagHelperContext.ExecutionContextOutputPropertyName}"; if (ContainsChildContent(chunk.Children)) { _writer .Write("if (!") .Write(tagHelperOutputAccessor) .Write(".") .Write(_tagHelperContext.TagHelperOutputIsContentModifiedPropertyName) .WriteLine(")"); using (_writer.BuildScope()) { _writer .Write("await ") .WriteInstanceMethodInvocation( ExecutionContextVariableName, _tagHelperContext.ExecutionContextSetOutputContentAsyncMethodName); } } _writer .WriteStartInstrumentationContext(_context, chunk.Association, isLiteral: false); if (!string.IsNullOrEmpty(_context.TargetWriterName)) { _writer .WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteToMethodName) .Write(_context.TargetWriterName) .WriteParameterSeparator(); } else { _writer.WriteStartMethodInvocation(_context.Host.GeneratedClassContext.WriteMethodName); } _writer .Write(tagHelperOutputAccessor) .WriteEndMethodInvocation() .WriteEndInstrumentationContext(_context); }
/// <summary> /// Renders the code for the given <paramref name="chunk"/>. /// </summary> /// <param name="chunk">A <see cref="TagHelperChunk"/> to render.</param> public void RenderTagHelper(TagHelperChunk chunk) { // Remove any duplicate TagHelperDescriptors that reference the same type name. Duplicates can occur when // multiple HtmlTargetElement attributes are on a TagHelper type and matches overlap for an HTML element. // Having more than one descriptor with the same TagHelper type results in generated code that runs // the same TagHelper X many times (instead of once) over a single HTML element. var tagHelperDescriptors = chunk.Descriptors.Distinct(TypeBasedTagHelperDescriptorComparer.Default); RenderBeginTagHelperScope(chunk.TagName, chunk.TagMode, chunk.Children); RenderTagHelpersCreation(chunk, tagHelperDescriptors); RenderAttributes(chunk.Attributes, tagHelperDescriptors); // No need to run anything in design time mode. if (!_designTimeMode) { RenderRunTagHelpers(); RenderTagHelperOutput(chunk); RenderEndTagHelpersScope(); } }
protected override void Visit(TagHelperChunk chunk) { if (Context.Host.DesignTimeMode) { return; } if (!_foundTagHelpers) { _foundTagHelpers = true; foreach (var tagHelperRuntimeNamespace in TagHelpersRuntimeNamespaces) { if (ImportedUsings.Add(tagHelperRuntimeNamespace)) { // If we find TagHelpers then we need to add the TagHelper runtime namespaces to our list of // usings. Writer.WriteUsing(tagHelperRuntimeNamespace); } } } }
/// <summary> /// Renders the code for the given <paramref name="chunk"/>. /// </summary> /// <param name="chunk">A <see cref="TagHelperChunk"/> to render.</param> public void RenderTagHelper(TagHelperChunk chunk) { // Remove any duplicate TagHelperDescriptors that reference the same type name. Duplicates can occur when // multiple TargetElement attributes are on a TagHelper type and matches overlap for an HTML element. // Having more than one descriptor with the same TagHelper type results in generated code that runs // the same TagHelper X many times (instead of once) over a single HTML element. var tagHelperDescriptors = chunk.Descriptors.Distinct(TypeBasedTagHelperDescriptorComparer.Default); RenderBeginTagHelperScope(chunk.TagName, chunk.SelfClosing, chunk.Children); RenderTagHelpersCreation(chunk, tagHelperDescriptors); RenderAttributes(chunk.Attributes, tagHelperDescriptors); // No need to run anything in design time mode. if (!_designTimeMode) { RenderRunTagHelpers(); RenderWriteTagHelperMethodCall(chunk); RenderEndTagHelpersScope(); } }
protected override void Visit(TagHelperChunk chunk) { if (!_foundTagHelpers) { _foundTagHelpers = true; var declareTagHelperFields = new DeclareTagHelperFields { UsedTagHelperTypeNames = _usedTagHelpers }; _context.Builder.Add(declareTagHelperFields); } foreach (var descriptor in chunk.Descriptors) { if (!_usedTagHelpers.Contains(descriptor.TypeName)) { _usedTagHelpers.Add(descriptor.TypeName); } } Accept(chunk.Children); }
private void RenderWriteTagHelperMethodCall(TagHelperChunk chunk) { _writer .WriteStartInstrumentationContext(_context, chunk.Association, isLiteral: false) .Write("await "); if (!string.IsNullOrEmpty(_context.TargetWriterName)) { _writer .WriteStartMethodInvocation(_tagHelperContext.WriteTagHelperToAsyncMethodName) .Write(_context.TargetWriterName) .WriteParameterSeparator(); } else { _writer.WriteStartMethodInvocation(_tagHelperContext.WriteTagHelperAsyncMethodName); } _writer .Write(ExecutionContextVariableName) .WriteEndMethodInvocation() .WriteEndInstrumentationContext(_context); }
/// <summary> /// Writes the TagHelperRunner initialization code to the Writer. /// </summary> /// <param name="chunk">The <see cref="TagHelperChunk"/>.</param> protected override void Visit(TagHelperChunk chunk) { if (!_foundTagHelpers && !Context.Host.DesignTimeMode) { _foundTagHelpers = true; Writer .WriteStartAssignment(CSharpTagHelperCodeRenderer.RunnerVariableName) .Write(CSharpTagHelperCodeRenderer.RunnerVariableName) .Write(" ?? ") .WriteStartNewObject("global::" + _tagHelperContext.RunnerTypeName) .WriteEndMethodInvocation(); Writer .WriteStartAssignment(CSharpTagHelperCodeRenderer.ScopeManagerVariableName) .Write(CSharpTagHelperCodeRenderer.ScopeManagerVariableName) .Write(" ?? ") .WriteStartNewObject("global::" + _tagHelperContext.ScopeManagerTypeName) .Write(_tagHelperContext.StartTagHelperWritingScopeMethodName) .WriteParameterSeparator() .Write(_tagHelperContext.EndTagHelperWritingScopeMethodName) .WriteEndMethodInvocation(); } }
private void PreAllocateUnboundTagHelperAttributes(TagHelperChunk chunk) { var boundAttributes = new HashSet <string>(StringComparer.OrdinalIgnoreCase); for (var i = 0; i < chunk.Attributes.Count; i++) { var attribute = chunk.Attributes[i]; var hasAssociatedDescriptors = chunk.Descriptors.Any(descriptor => descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Key))); // If there's no descriptors associated or we're hitting a bound attribute a second time. if (!hasAssociatedDescriptors || !boundAttributes.Add(attribute.Key)) { string preAllocatedAttributeVariableName = null; if (attribute.Value == null) { var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Key, value: null); if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName)) { Writer .Write("private static readonly global::") .Write(_tagHelperContext.TagHelperAttributeTypeName) .Write(" ") .Write(preAllocatedAttributeVariableName) .Write(" = ") .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName) .WriteStringLiteral(attribute.Key) .WriteEndMethodInvocation(); } } else { string plainText; if (CSharpTagHelperCodeRenderer.TryGetPlainTextValue(attribute.Value, out plainText)) { var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Key, plainText); if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName)) { Writer .Write("private static readonly global::") .Write(_tagHelperContext.TagHelperAttributeTypeName) .Write(" ") .Write(preAllocatedAttributeVariableName) .Write(" = ") .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName) .WriteStringLiteral(attribute.Key) .WriteParameterSeparator() .WriteStartNewObject("global::" + _tagHelperContext.EncodedHtmlStringTypeName) .WriteStringLiteral(plainText) .WriteEndMethodInvocation(endLine: false) .WriteEndMethodInvocation(); } } } if (preAllocatedAttributeVariableName != null) { chunk.Attributes[i] = new KeyValuePair <string, Chunk>( attribute.Key, new PreallocatedTagHelperAttributeChunk { AttributeVariableAccessor = preAllocatedAttributeVariableName }); } } } }
private void RenderTagHelpersCreation( TagHelperChunk chunk, IEnumerable <TagHelperDescriptor> tagHelperDescriptors) { // This is to maintain value accessors for attributes when creating the TagHelpers. // Ultimately it enables us to do scenarios like this: // myTagHelper1.Foo = DateTime.Now; // myTagHelper2.Foo = myTagHelper1.Foo; var htmlAttributeValues = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var tagHelperDescriptor in tagHelperDescriptors) { var tagHelperVariableName = GetVariableName(tagHelperDescriptor); // Create the tag helper _writer.WriteStartAssignment(tagHelperVariableName) .WriteStartMethodInvocation(_tagHelperContext.CreateTagHelperMethodName, tagHelperDescriptor.TypeName) .WriteEndMethodInvocation(); // Execution contexts and throwing errors for null dictionary properties are a runtime feature. if (_designTimeMode) { continue; } _writer.WriteInstanceMethodInvocation( ExecutionContextVariableName, _tagHelperContext.ExecutionContextAddMethodName, tagHelperVariableName); // Track dictionary properties we have confirmed are non-null. var confirmedDictionaries = new HashSet <string>(StringComparer.Ordinal); // Ensure that all created TagHelpers have initialized dictionary bound properties which are used // via TagHelper indexers. foreach (var chunkAttribute in chunk.Attributes) { var associatedAttributeDescriptor = tagHelperDescriptor.Attributes.FirstOrDefault( attributeDescriptor => attributeDescriptor.IsNameMatch(chunkAttribute.Key)); if (associatedAttributeDescriptor != null && associatedAttributeDescriptor.IsIndexer && confirmedDictionaries.Add(associatedAttributeDescriptor.PropertyName)) { // Throw a reasonable Exception at runtime if the dictionary property is null. _writer .Write("if (") .Write(tagHelperVariableName) .Write(".") .Write(associatedAttributeDescriptor.PropertyName) .WriteLine(" == null)"); using (_writer.BuildScope()) { // System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName // of InvalidOperationException type. _writer .Write("throw ") .WriteStartNewObject(nameof(InvalidOperationException)) .WriteStartMethodInvocation(_tagHelperContext.FormatInvalidIndexerAssignmentMethodName) .WriteStringLiteral(chunkAttribute.Key) .WriteParameterSeparator() .WriteStringLiteral(tagHelperDescriptor.TypeName) .WriteParameterSeparator() .WriteStringLiteral(associatedAttributeDescriptor.PropertyName) .WriteEndMethodInvocation(endLine: false) // End of method call .WriteEndMethodInvocation(endLine: true); // End of new expression / throw statement } } } } }
protected virtual void Visit(TagHelperChunk chunk) { }
private void PreAllocateTagHelperAttributes(TagHelperChunk chunk) { var boundAttributes = new HashSet <string>(StringComparer.OrdinalIgnoreCase); for (var i = 0; i < chunk.Attributes.Count; i++) { var attribute = chunk.Attributes[i]; var associatedAttributeDescriptors = chunk.Descriptors.SelectMany(descriptor => descriptor.Attributes) .Where(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name)); // If there's no descriptors associated or is a repeated attribute with same name as a bound attribute, // it is considered as an unbound attribute. var isUnBoundAttribute = !associatedAttributeDescriptors.Any() || !boundAttributes.Add(attribute.Name); // Perf: We will preallocate TagHelperAttribute for unbound attributes and simple bound string valued attributes. if (isUnBoundAttribute || CanPreallocateBoundAttribute(associatedAttributeDescriptors, attribute)) { string preAllocatedAttributeVariableName = null; if (attribute.ValueStyle == HtmlAttributeValueStyle.Minimized) { Debug.Assert(attribute.Value == null); var preAllocatedAttributeKey = new TagHelperAttributeKey( attribute.Name, value: null, unBoundAttribute: isUnBoundAttribute, valueStyle: attribute.ValueStyle); if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName)) { Writer .Write("private static readonly global::") .Write(_tagHelperContext.TagHelperAttributeTypeName) .Write(" ") .Write(preAllocatedAttributeVariableName) .Write(" = ") .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName) .WriteStringLiteral(attribute.Name) .WriteEndMethodInvocation(); } } else { Debug.Assert(attribute.Value != null); string plainText; if (CSharpTagHelperCodeRenderer.TryGetPlainTextValue(attribute.Value, out plainText)) { var preAllocatedAttributeKey = new TagHelperAttributeKey(attribute.Name, plainText, isUnBoundAttribute, attribute.ValueStyle); if (TryCachePreallocatedVariableName(preAllocatedAttributeKey, out preAllocatedAttributeVariableName)) { Writer .Write("private static readonly global::") .Write(_tagHelperContext.TagHelperAttributeTypeName) .Write(" ") .Write(preAllocatedAttributeVariableName) .Write(" = ") .WriteStartNewObject("global::" + _tagHelperContext.TagHelperAttributeTypeName) .WriteStringLiteral(attribute.Name) .WriteParameterSeparator(); if (isUnBoundAttribute) { // For unbound attributes, we need to create HtmlString. Writer .WriteStartNewObject("global::" + _tagHelperContext.EncodedHtmlStringTypeName) .WriteStringLiteral(plainText) .WriteEndMethodInvocation(endLine: false); } else { Writer.WriteStringLiteral(plainText); } Writer .WriteParameterSeparator() .Write($"global::{typeof(HtmlAttributeValueStyle).FullName}.{attribute.ValueStyle}") .WriteEndMethodInvocation(); } } } if (preAllocatedAttributeVariableName != null) { chunk.Attributes[i] = new TagHelperAttributeTracker( attribute.Name, new PreallocatedTagHelperAttributeChunk { AttributeVariableAccessor = preAllocatedAttributeVariableName }, attribute.ValueStyle); } } } }
protected override void Visit(TagHelperChunk chunk) { }
protected abstract void Visit(TagHelperChunk chunk);
/// <summary> /// Renders the code for the given <paramref name="chunk"/>. /// </summary> /// <param name="chunk">A <see cref="TagHelperChunk"/> to render.</param> public void RenderTagHelper(TagHelperChunk chunk) { var tagHelperDescriptors = chunk.Descriptors; // Find the first content behavior that doesn't have a content behavior of None. // The resolver restricts content behavior collisions so the first one that's not None will be // the content behavior we need to abide by. None can work in unison with other ContentBehaviors. var contentBehavior = tagHelperDescriptors.Select(descriptor => descriptor.ContentBehavior) .FirstOrDefault( behavior => behavior != ContentBehavior.None); RenderBeginTagHelperScope(chunk.TagName); RenderTagHelpersCreation(chunk); var attributeDescriptors = tagHelperDescriptors.SelectMany(descriptor => descriptor.Attributes); var boundHTMLAttributes = attributeDescriptors.Select(descriptor => descriptor.Name); var htmlAttributes = chunk.Attributes; var unboundHTMLAttributes = htmlAttributes.Where(htmlAttribute => !boundHTMLAttributes.Contains(htmlAttribute.Key, StringComparer.OrdinalIgnoreCase)); RenderUnboundHTMLAttributes(unboundHTMLAttributes); switch (contentBehavior) { case ContentBehavior.None: RenderRunTagHelpers(bufferedBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); RenderTagHelperBody(chunk.Children, bufferBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); break; case ContentBehavior.Append: RenderRunTagHelpers(bufferedBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); RenderTagHelperBody(chunk.Children, bufferBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); break; case ContentBehavior.Prepend: RenderRunTagHelpers(bufferedBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); RenderTagHelperBody(chunk.Children, bufferBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); break; case ContentBehavior.Replace: RenderRunTagHelpers(bufferedBody: false); RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); break; case ContentBehavior.Modify: RenderTagHelperBody(chunk.Children, bufferBody: true); RenderRunTagHelpers(bufferedBody: true); RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); break; } RenderEndTagHelpersScope(); }
protected override void Visit(TagHelperChunk chunk) { TagHelperRenderer.RenderTagHelper(chunk); }
private void RenderTagHelpersCreation( TagHelperChunk chunk, IEnumerable<TagHelperDescriptor> tagHelperDescriptors) { // This is to maintain value accessors for attributes when creating the TagHelpers. // Ultimately it enables us to do scenarios like this: // myTagHelper1.Foo = DateTime.Now; // myTagHelper2.Foo = myTagHelper1.Foo; var htmlAttributeValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (var tagHelperDescriptor in tagHelperDescriptors) { var tagHelperVariableName = GetVariableName(tagHelperDescriptor); // Create the tag helper _writer.WriteStartAssignment(tagHelperVariableName) .WriteStartMethodInvocation( _tagHelperContext.CreateTagHelperMethodName, "global::" + tagHelperDescriptor.TypeName) .WriteEndMethodInvocation(); // Execution contexts and throwing errors for null dictionary properties are a runtime feature. if (_designTimeMode) { continue; } _writer.WriteInstanceMethodInvocation( ExecutionContextVariableName, _tagHelperContext.ExecutionContextAddMethodName, tagHelperVariableName); // Track dictionary properties we have confirmed are non-null. var confirmedDictionaries = new HashSet<string>(StringComparer.Ordinal); // Ensure that all created TagHelpers have initialized dictionary bound properties which are used // via TagHelper indexers. foreach (var chunkAttribute in chunk.Attributes) { var associatedAttributeDescriptor = tagHelperDescriptor.Attributes.FirstOrDefault( attributeDescriptor => attributeDescriptor.IsNameMatch(chunkAttribute.Name)); if (associatedAttributeDescriptor != null && associatedAttributeDescriptor.IsIndexer && confirmedDictionaries.Add(associatedAttributeDescriptor.PropertyName)) { // Throw a reasonable Exception at runtime if the dictionary property is null. _writer .Write("if (") .Write(tagHelperVariableName) .Write(".") .Write(associatedAttributeDescriptor.PropertyName) .WriteLine(" == null)"); using (_writer.BuildScope()) { // System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName // of InvalidOperationException type. _writer .Write("throw ") .WriteStartNewObject(nameof(InvalidOperationException)) .WriteStartMethodInvocation(_tagHelperContext.FormatInvalidIndexerAssignmentMethodName) .WriteStringLiteral(chunkAttribute.Name) .WriteParameterSeparator() .WriteStringLiteral(tagHelperDescriptor.TypeName) .WriteParameterSeparator() .WriteStringLiteral(associatedAttributeDescriptor.PropertyName) .WriteEndMethodInvocation(endLine: false) // End of method call .WriteEndMethodInvocation(endLine: true); // End of new expression / throw statement } } } } }