/// <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();
                RenderWriteTagHelperMethodCall(chunk);
                RenderEndTagHelpersScope();
            }
        }
        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
                        }
                    }
                }
            }
        }
        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);
        }
        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(tagHelperOutputAccessor)
                        .Write(".")
                        .WriteStartAssignment(_tagHelperContext.TagHelperOutputContentPropertyName)
                        .Write("await ")
                        .WriteInstanceMethodInvocation(
                            tagHelperOutputAccessor,
                            _tagHelperContext.TagHelperOutputGetChildContentAsyncMethodName);
                }
            }

            _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);
        }