public void RenderAttributeValue_RendersModelExpressionsCorrectly(string modelExpressionType, string propertyType, string expectedValue) { // Arrange var renderer = new MvcTagHelperAttributeValueCodeRenderer( new GeneratedTagHelperAttributeContext { ModelExpressionTypeName = modelExpressionType, CreateModelExpressionMethodName = "SomeMethod" }); var attributeDescriptor = new TagHelperAttributeDescriptor("MyAttribute", "SomeProperty", propertyType); var writer = new CSharpCodeWriter(); var generatorContext = new CodeGeneratorContext(host: null, className: string.Empty, rootNamespace: string.Empty, sourceFile: string.Empty, shouldGenerateLinePragmas: true); var context = new CodeBuilderContext(generatorContext); // Act renderer.RenderAttributeValue(attributeDescriptor, writer, context, (codeWriter) => { codeWriter.Write("MyValue"); }); // Assert Assert.Equal(expectedValue, writer.GenerateCode()); }
private static bool IsStringAttribute(TagHelperAttributeDescriptor attributeDescriptor) { return(string.Equals( attributeDescriptor.TypeName, typeof(string).FullName, StringComparison.Ordinal)); }
private TagHelperAttributeDescriptor GetIndexerAttributeDescriptor(ParameterInfo parameter, string name) { var dictionaryTypeArguments = ClosedGenericMatcher.ExtractGenericInterface( parameter.ParameterType, typeof(IDictionary <,>)) ?.GenericTypeArguments .Select(t => t.IsGenericParameter ? null : t) .ToArray(); if (dictionaryTypeArguments?[0] != typeof(string)) { return(null); } var type = dictionaryTypeArguments[1]; var descriptor = new TagHelperAttributeDescriptor { Name = name + "-", PropertyName = parameter.Name, TypeName = GetCSharpTypeName(type), IsEnum = type.GetTypeInfo().IsEnum, IsIndexer = true }; return(descriptor); }
// Internal for testing. internal static bool ValidateTagHelperAttributeDescriptor( TagHelperAttributeDescriptor attributeDescriptor, ITypeInfo parentType, ErrorSink errorSink) { string nameOrPrefix; if (attributeDescriptor.IsIndexer) { nameOrPrefix = Resources.TagHelperDescriptorFactory_Prefix; } else if (string.IsNullOrEmpty(attributeDescriptor.Name)) { errorSink.OnError( SourceLocation.Zero, Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty( parentType.FullName, attributeDescriptor.PropertyName), length: 0); return(false); } else { nameOrPrefix = Resources.TagHelperDescriptorFactory_Name; } return(ValidateTagHelperAttributeNameOrPrefix( attributeDescriptor.Name, parentType, attributeDescriptor.PropertyName, errorSink, nameOrPrefix)); }
/// <inheritdoc /> /// <remarks>If the attribute being rendered is of the type /// <see cref="GeneratedTagHelperAttributeContext.ModelExpressionTypeName"/>, then a model expression will be /// created by calling into <see cref="GeneratedTagHelperAttributeContext.CreateModelExpressionMethodName"/>. /// </remarks> public override void RenderAttributeValue([NotNull] TagHelperAttributeDescriptor attributeDescriptor, [NotNull] CSharpCodeWriter writer, [NotNull] CodeBuilderContext codeBuilderContext, [NotNull] Action<CSharpCodeWriter> renderAttributeValue, bool complexValue) { if (attributeDescriptor.TypeName.Equals(_context.ModelExpressionTypeName, StringComparison.Ordinal)) { writer .WriteStartMethodInvocation(_context.CreateModelExpressionMethodName) .Write(ModelLambdaVariableName) .Write(" => "); if (!complexValue) { writer .Write(ModelLambdaVariableName) .Write("."); } renderAttributeValue(writer); writer.WriteEndMethodInvocation(endLine: false); } else { base.RenderAttributeValue( attributeDescriptor, writer, codeBuilderContext, renderAttributeValue, complexValue); } }
/// <summary> /// Called during Razor's code generation process to generate code that instantiates the value of the tag /// helper's property. Last value written should not be or end with a semicolon. /// </summary> /// <param name="attributeDescriptor"> /// The <see cref="TagHelperAttributeDescriptor"/> to generate code for. /// </param> /// <param name="writer">The <see cref="CSharpCodeWriter"/> that's used to write code.</param> /// <param name="context">A <see cref="Chunks.Generators.ChunkGeneratorContext"/> instance that contains /// information about the current code generation process.</param> /// <param name="renderAttributeValue"> /// <see cref="Action"/> that renders the raw value of the HTML attribute. /// </param> /// <param name="complexValue"> /// Indicates whether or not the source attribute value contains more than simple text. <c>false</c> for plain /// C# expressions e.g. <c>"PropertyName"</c>. <c>true</c> if the attribute value contain at least one in-line /// Razor construct e.g. <c>"@(@readonly)"</c>. /// </param> public virtual void RenderAttributeValue( TagHelperAttributeDescriptor attributeDescriptor, CSharpCodeWriter writer, CodeGeneratorContext context, Action <CSharpCodeWriter> renderAttributeValue, bool complexValue) { if (attributeDescriptor == null) { throw new ArgumentNullException(nameof(attributeDescriptor)); } if (writer == null) { throw new ArgumentNullException(nameof(writer)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (renderAttributeValue == null) { throw new ArgumentNullException(nameof(renderAttributeValue)); } renderAttributeValue(writer); }
private void RenderBufferedAttributeValue(TagHelperAttributeDescriptor attributeDescriptor) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { RenderBufferedAttributeValueAccessor(writer); }); }
/// <summary> /// Called during Razor's code generation process to generate code that instantiates the value of the tag /// helper's property. Last value written should not be or end with a semicolon. /// </summary> /// <param name="attributeDescriptor"> /// The <see cref="TagHelperAttributeDescriptor"/> to generate code for. /// </param> /// <param name="writer">The <see cref="CSharpCodeWriter"/> that's used to write code.</param> /// <param name="context">A <see cref="Chunks.Generators.ChunkGeneratorContext"/> instance that contains /// information about the current code generation process.</param> /// <param name="renderAttributeValue"> /// <see cref="Action"/> that renders the raw value of the HTML attribute. /// </param> /// <param name="complexValue"> /// Indicates whether or not the source attribute value contains more than simple text. <c>false</c> for plain /// C# expressions e.g. <c>"PropertyName"</c>. <c>true</c> if the attribute value contain at least one in-line /// Razor construct e.g. <c>"@(@readonly)"</c>. /// </param> public virtual void RenderAttributeValue( [NotNull] TagHelperAttributeDescriptor attributeDescriptor, [NotNull] CSharpCodeWriter writer, [NotNull] CodeGeneratorContext context, [NotNull] Action <CSharpCodeWriter> renderAttributeValue, bool complexValue) { renderAttributeValue(writer); }
private void RenderRawAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { writer.Write(value); }); }
public override void RenderAttributeValue([NotNull] TagHelperAttributeDescriptor attributeInfo, [NotNull] CSharpCodeWriter writer, [NotNull] CodeBuilderContext context, [NotNull] Action <CSharpCodeWriter> renderAttributeValue) { writer.Write("**From custom attribute code renderer**: "); base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue); }
private void RenderQuotedAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { writer.WriteStringLiteral(value); }); }
/// <inheritdoc /> /// <remarks>If the attribute being rendered is of the type /// <see cref="GeneratedTagHelperAttributeContext.ModelExpressionTypeName"/>, then a model expression will be /// created by calling into <see cref="GeneratedTagHelperAttributeContext.CreateModelExpressionMethodName"/>. /// </remarks> public override void RenderAttributeValue( TagHelperAttributeDescriptor attributeDescriptor, CSharpCodeWriter writer, CodeGeneratorContext codeGeneratorContext, Action <CSharpCodeWriter> renderAttributeValue, bool complexValue) { if (attributeDescriptor == null) { throw new ArgumentNullException(nameof(attributeDescriptor)); } if (writer == null) { throw new ArgumentNullException(nameof(writer)); } if (codeGeneratorContext == null) { throw new ArgumentNullException(nameof(codeGeneratorContext)); } if (renderAttributeValue == null) { throw new ArgumentNullException(nameof(renderAttributeValue)); } if (attributeDescriptor.TypeName.Equals(_context.ModelExpressionTypeName, StringComparison.Ordinal)) { writer .WriteStartInstanceMethodInvocation(_context.ModelExpressionProviderPropertyName, _context.CreateModelExpressionMethodName) .Write(_context.ViewDataPropertyName) .WriteParameterSeparator() .Write(ModelLambdaVariableName) .Write(" => "); if (!complexValue) { writer .Write(ModelLambdaVariableName) .Write("."); } renderAttributeValue(writer); writer.WriteEndMethodInvocation(endLine: false); } else { base.RenderAttributeValue( attributeDescriptor, writer, codeGeneratorContext, renderAttributeValue, complexValue); } }
public override void RenderAttributeValue( TagHelperAttributeDescriptor attributeInfo, CSharpCodeWriter writer, CodeGeneratorContext context, Action <CSharpCodeWriter> renderAttributeValue, bool complexValue) { writer.Write("**From custom attribute code renderer**: "); base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue, complexValue); }
private void RenderBufferedAttributeValue(TagHelperAttributeDescriptor attributeDescriptor) { // Pass complexValue: false because variable.ToString() replaces any original complexity in the expression. RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { RenderBufferedAttributeValueAccessor(writer); }, complexValue: false); }
private void RenderAttributeValue(TagHelperAttributeDescriptor attributeDescriptor, Action <CSharpCodeWriter> valueRenderer, bool complexValue) { AttributeValueCodeRenderer.RenderAttributeValue( attributeDescriptor, _writer, _context, valueRenderer, complexValue); }
private static string GetTagHelperPropertyAccessor( string tagHelperVariableName, string attributeName, TagHelperAttributeDescriptor associatedDescriptor) { var propertyAccessor = $"{tagHelperVariableName}.{associatedDescriptor.PropertyName}"; if (associatedDescriptor.IsIndexer) { var dictionaryKey = attributeName.Substring(associatedDescriptor.Name.Length); propertyAccessor += $"[\"{dictionaryKey}\"]"; } return(propertyAccessor); }
private void RenderRawAttributeValue( Chunk attributeValueChunk, TagHelperAttributeDescriptor attributeDescriptor, bool isPlainTextValue) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { var visitor = new CSharpTagHelperAttributeValueVisitor(writer, _context, attributeDescriptor.TypeName); visitor.Accept(attributeValueChunk); }, complexValue: !isPlainTextValue); }
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); }
public void TagHelperAttributeDescriptor_IsStringPropertySetCorrectly( Type attributeType, bool isIndexer, bool expectedIsStringProperty) { // Arrange var attributeDescriptor = new TagHelperAttributeDescriptor { Name = "someAttribute", PropertyName = "someProperty", TypeName = attributeType.FullName, IsIndexer = isIndexer }; // Assert Assert.Equal(expectedIsStringProperty, attributeDescriptor.IsStringProperty); }
private void SetAttributeDescriptors(ViewComponentDescriptor viewComponentDescriptor, TagHelperDescriptor tagHelperDescriptor) { var methodParameters = viewComponentDescriptor.MethodInfo.GetParameters(); var attributeDescriptors = new List <TagHelperAttributeDescriptor>(); var indexerDescriptors = new List <TagHelperAttributeDescriptor>(); var requiredAttributeDescriptors = new List <TagHelperRequiredAttributeDescriptor>(); foreach (var parameter in methodParameters) { var lowerKebabName = TagHelperDescriptorFactory.ToHtmlCase(parameter.Name); var typeName = GetCSharpTypeName(parameter.ParameterType); var descriptor = new TagHelperAttributeDescriptor { Name = lowerKebabName, PropertyName = parameter.Name, TypeName = typeName }; descriptor.IsEnum = parameter.ParameterType.GetTypeInfo().IsEnum; descriptor.IsIndexer = false; attributeDescriptors.Add(descriptor); var indexerDescriptor = GetIndexerAttributeDescriptor(parameter, lowerKebabName); if (indexerDescriptor != null) { indexerDescriptors.Add(indexerDescriptor); } else { // Set required attributes only for non-indexer attributes. Indexer attributes can't be required attributes // because there are two ways of setting values for the attribute. requiredAttributeDescriptors.Add(new TagHelperRequiredAttributeDescriptor { Name = lowerKebabName }); } } attributeDescriptors.AddRange(indexerDescriptors); tagHelperDescriptor.Attributes = attributeDescriptors; tagHelperDescriptor.RequiredAttributes = requiredAttributeDescriptors; }
public void RenderAttributeValue_RendersModelExpressionsCorrectly( string modelExpressionType, string propertyType, string expectedValue) { // Arrange var renderer = new MvcTagHelperAttributeValueCodeRenderer( new GeneratedTagHelperAttributeContext { ModelExpressionTypeName = modelExpressionType, CreateModelExpressionMethodName = "SomeMethod", ModelExpressionProviderPropertyName = "Provider", ViewDataPropertyName = "ViewData" }); var attributeDescriptor = new TagHelperAttributeDescriptor { Name = "MyAttribute", PropertyName = "SomeProperty", TypeName = propertyType, }; var writer = new CSharpCodeWriter(); var generatorContext = new ChunkGeneratorContext( host: null, className: string.Empty, rootNamespace: string.Empty, sourceFile: string.Empty, shouldGenerateLinePragmas: true); var errorSink = new ErrorSink(); var context = new CodeGeneratorContext(generatorContext, errorSink); // Act renderer.RenderAttributeValue(attributeDescriptor, writer, context, (codeWriter) => { codeWriter.Write("MyValue"); }, complexValue: false); // Assert Assert.Equal(expectedValue, writer.GenerateCode()); }
private void RenderCodeAttributeValue( Chunk attributeValueChunk, TagHelperAttributeDescriptor attributeDescriptor, bool isPlainTextValue) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { if (attributeDescriptor.IsEnum && isPlainTextValue) { writer .Write("global::") .Write(attributeDescriptor.TypeName) .Write("."); } var visitor = new CSharpTagHelperAttributeValueVisitor(writer, _context, attributeDescriptor.TypeName); visitor.Accept(attributeValueChunk); }, complexValue: !isPlainTextValue); }
private static bool IsStringAttribute(TagHelperAttributeDescriptor attributeDescriptor) { return(attributeDescriptor.PropertyInfo.PropertyType == typeof(string)); }
// Render assignment of attribute value to the value accessor. private void RenderNewAttributeValueAssignment( TagHelperAttributeDescriptor attributeDescriptor, bool bufferableAttribute, Chunk attributeValueChunk, string valueAccessor) { // Plain text values are non Razor code (@DateTime.Now) values. If an attribute is bufferable it // may be more than just a plain text value, it may also contain Razor code which is why we attempt // to retrieve a plain text value here. string textValue; var isPlainTextValue = TryGetPlainTextValue(attributeValueChunk, out textValue); if (bufferableAttribute) { if (!isPlainTextValue) { // If we haven't recorded a value and we need to buffer an attribute value and the value is not // plain text then we need to prepare the value prior to setting it below. BuildBufferedWritingScope(attributeValueChunk, htmlEncodeValues: false); } _writer.WriteStartAssignment(valueAccessor); if (isPlainTextValue) { // If the attribute is bufferable but has a plain text value that means the value // is a string which needs to be surrounded in quotes. RenderQuotedAttributeValue(textValue, attributeDescriptor); } else { // The value contains more than plain text e.g. stringAttribute ="Time: @DateTime.Now". RenderBufferedAttributeValue(attributeDescriptor); } _writer.WriteLine(";"); } else { // Write out simple assignment for non-string property value. Try to keep the whole // statement together and the #line pragma correct to make debugging possible. using (var lineMapper = new CSharpLineMappingWriter( _writer, attributeValueChunk.Association.Start, _context.SourceFile)) { // Place the assignment LHS to align RHS with original attribute value's indentation. // Unfortunately originalIndent is incorrect if original line contains tabs. Unable to // use a CSharpPaddingBuilder because the Association has no Previous node; lost the // original Span sequence when the parse tree was rewritten. var originalIndent = attributeValueChunk.Start.CharacterIndex; var generatedLength = valueAccessor.Length + " = ".Length; var newIndent = originalIndent - generatedLength; if (newIndent > 0) { _writer.Indent(newIndent); } _writer.WriteStartAssignment(valueAccessor); lineMapper.MarkLineMappingStart(); // Write out bare expression for this attribute value. Property is not a string. // So quoting or buffering are not helpful. RenderRawAttributeValue(attributeValueChunk, attributeDescriptor, isPlainTextValue); // End the assignment to the attribute. lineMapper.MarkLineMappingEnd(); _writer.WriteLine(";"); } } }
private string RenderBoundAttribute( string attributeName, Chunk attributeValueChunk, string tagHelperVariableName, string previousValueAccessor, TagHelperAttributeDescriptor attributeDescriptor) { var currentValueAccessor = string.Format( CultureInfo.InvariantCulture, "{0}.{1}", tagHelperVariableName, attributeDescriptor.PropertyName); if (attributeDescriptor.IsIndexer) { var dictionaryKey = attributeName.Substring(attributeDescriptor.Name.Length); currentValueAccessor += $"[\"{dictionaryKey}\"]"; } // If this attribute value has not been seen before, need to record its value. if (previousValueAccessor == null) { // Bufferable attributes are attributes that can have Razor code inside of them. Such // attributes have string values and may be calculated using a temporary TextWriter or other // buffer. var bufferableAttribute = attributeDescriptor.IsStringProperty; RenderNewAttributeValueAssignment( attributeDescriptor, bufferableAttribute, attributeValueChunk, currentValueAccessor); if (_designTimeMode) { // Execution contexts are a runtime feature. return(currentValueAccessor); } // We need to inform the context of the attribute value. _writer .WriteStartInstanceMethodInvocation( ExecutionContextVariableName, _tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName) .WriteStringLiteral(attributeName) .WriteParameterSeparator() .Write(currentValueAccessor) .WriteEndMethodInvocation(); return(currentValueAccessor); } else { // The attribute value has already been determined and accessor was passed to us as // previousValueAccessor, we don't want to evaluate the value twice so lets just use the // previousValueLocation. _writer .WriteStartAssignment(currentValueAccessor) .Write(previousValueAccessor) .WriteLine(";"); return(previousValueAccessor); } }
public void ValidateTagHelperAttributeDescriptor_WithValidPrefix_ReturnsTrue(string prefix) { // Arrange var descriptor = new TagHelperAttributeDescriptor { Name = prefix, PropertyName = "ValidProperty", TypeName = "PropertyType", IsIndexer = true }; var errorSink = new ErrorSink(); // Act var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( descriptor, GetTypeInfo(typeof(MultiTagTagHelper)), errorSink); // Assert Assert.True(result); Assert.Empty(errorSink.Errors); }
public void ValidateTagHelperAttributeDescriptor_WithInvalidPrefix_AddsExpectedErrors( string prefix, string[] expectedErrorMessages) { // Arrange var descriptor = new TagHelperAttributeDescriptor { Name = prefix, PropertyName = "InvalidProperty", TypeName = "ValuesType", IsIndexer = true }; var errorSink = new ErrorSink(); // Act var result = TagHelperDescriptorFactory.ValidateTagHelperAttributeDescriptor( descriptor, GetTypeInfo(typeof(MultiTagTagHelper)), errorSink); // Assert Assert.False(result); var errors = errorSink.Errors.ToArray(); Assert.Equal(expectedErrorMessages.Length, errors.Length); for (var i = 0; i < expectedErrorMessages.Length; i++) { Assert.Equal(0, errors[i].Length); Assert.Equal(SourceLocation.Zero, errors[i].Location); Assert.Equal(expectedErrorMessages[i], errors[i].Message, StringComparer.Ordinal); } }
public override void RenderAttributeValue( TagHelperAttributeDescriptor attributeInfo, CSharpCodeWriter writer, CodeGeneratorContext context, Action<CSharpCodeWriter> renderAttributeValue, bool complexValue) { writer.Write("**From custom attribute code renderer**: "); base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue, complexValue); }
private string RenderBoundAttribute( string attributeName, Chunk attributeValueChunk, string tagHelperVariableName, string previousValueAccessor, TagHelperAttributeDescriptor attributeDescriptor) { var currentValueAccessor = string.Format( CultureInfo.InvariantCulture, "{0}.{1}", tagHelperVariableName, attributeDescriptor.PropertyName); if (attributeDescriptor.IsIndexer) { var dictionaryKey = attributeName.Substring(attributeDescriptor.Name.Length); currentValueAccessor += $"[\"{dictionaryKey}\"]"; } // If this attribute value has not been seen before, need to record its value. if (previousValueAccessor == null) { // Bufferable attributes are attributes that can have Razor code inside of them. Such // attributes have string values and may be calculated using a temporary TextWriter or other // buffer. var bufferableAttribute = attributeDescriptor.IsStringProperty; RenderNewAttributeValueAssignment( attributeDescriptor, bufferableAttribute, attributeValueChunk, currentValueAccessor); if (_designTimeMode) { // Execution contexts are a runtime feature. return currentValueAccessor; } // We need to inform the context of the attribute value. _writer .WriteStartInstanceMethodInvocation( ExecutionContextVariableName, _tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName) .WriteStringLiteral(attributeName) .WriteParameterSeparator() .Write(currentValueAccessor) .WriteEndMethodInvocation(); return currentValueAccessor; } else { // The attribute value has already been determined and accessor was passed to us as // previousValueAccessor, we don't want to evaluate the value twice so lets just use the // previousValueLocation. _writer .WriteStartAssignment(currentValueAccessor) .Write(previousValueAccessor) .WriteLine(";"); return previousValueAccessor; } }
// Render assignment of attribute value to the value accessor. private void RenderNewAttributeValueAssignment( TagHelperAttributeDescriptor attributeDescriptor, bool bufferableAttribute, Chunk attributeValueChunk, string valueAccessor) { // Plain text values are non Razor code (@DateTime.Now) values. If an attribute is bufferable it // may be more than just a plain text value, it may also contain Razor code which is why we attempt // to retrieve a plain text value here. string textValue; var isPlainTextValue = TryGetPlainTextValue(attributeValueChunk, out textValue); if (bufferableAttribute) { if (!isPlainTextValue) { // If we haven't recorded a value and we need to buffer an attribute value and the value is not // plain text then we need to prepare the value prior to setting it below. BuildBufferedWritingScope(attributeValueChunk, htmlEncodeValues: false); } _writer.WriteStartAssignment(valueAccessor); if (isPlainTextValue) { // If the attribute is bufferable but has a plain text value that means the value // is a string which needs to be surrounded in quotes. RenderQuotedAttributeValue(textValue, attributeDescriptor); } else { // The value contains more than plain text e.g. stringAttribute ="Time: @DateTime.Now". RenderBufferedAttributeValue(attributeDescriptor); } _writer.WriteLine(";"); } else { // Write out simple assignment for non-string property value. Try to keep the whole // statement together and the #line pragma correct to make debugging possible. using (var lineMapper = new CSharpLineMappingWriter( _writer, attributeValueChunk.Association.Start, _context.SourceFile)) { // Place the assignment LHS to align RHS with original attribute value's indentation. // Unfortunately originalIndent is incorrect if original line contains tabs. Unable to // use a CSharpPaddingBuilder because the Association has no Previous node; lost the // original Span sequence when the parse tree was rewritten. var originalIndent = attributeValueChunk.Start.CharacterIndex; var generatedLength = valueAccessor.Length + " = ".Length; var newIndent = originalIndent - generatedLength; if (newIndent > 0) { _writer.Indent(newIndent); } _writer.WriteStartAssignment(valueAccessor); lineMapper.MarkLineMappingStart(); // Write out bare expression for this attribute value. Property is not a string. // So quoting or buffering are not helpful. RenderRawAttributeValue(attributeValueChunk, attributeDescriptor, isPlainTextValue); // End the assignment to the attribute. lineMapper.MarkLineMappingEnd(); _writer.WriteLine(";"); } } }
private void RenderAttributeValue(TagHelperAttributeDescriptor attributeDescriptor, Action<CSharpCodeWriter> valueRenderer, bool complexValue) { AttributeValueCodeRenderer.RenderAttributeValue( attributeDescriptor, _writer, _context, valueRenderer, complexValue); }
private void RenderRawAttributeValue( Chunk attributeValueChunk, TagHelperAttributeDescriptor attributeDescriptor, bool isPlainTextValue) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { var visitor = new CSharpTagHelperAttributeValueVisitor(writer, _context, attributeDescriptor.TypeName); visitor.Accept(attributeValueChunk); }, complexValue: !isPlainTextValue); }
private static IEnumerable <TagHelperAttributeDescriptor> GetAttributeDescriptors( ITypeInfo type, bool designTime, ErrorSink errorSink) { var attributeDescriptors = new List <TagHelperAttributeDescriptor>(); // Keep indexer descriptors separate to avoid sorting the combined list later. var indexerDescriptors = new List <TagHelperAttributeDescriptor>(); var accessibleProperties = type.Properties.Where(IsAccessibleProperty); foreach (var property in accessibleProperties) { if (ShouldSkipDescriptorCreation(designTime, property)) { continue; } var attributeNameAttribute = property .GetCustomAttributes <HtmlAttributeNameAttribute>() .FirstOrDefault(); var hasExplicitName = attributeNameAttribute != null && !string.IsNullOrEmpty(attributeNameAttribute.Name); var attributeName = hasExplicitName ? attributeNameAttribute.Name : ToHtmlCase(property.Name); TagHelperAttributeDescriptor mainDescriptor = null; if (property.HasPublicSetter) { mainDescriptor = ToAttributeDescriptor(property, attributeName, designTime); if (!ValidateTagHelperAttributeDescriptor(mainDescriptor, type, errorSink)) { // HtmlAttributeNameAttribute.Name is invalid. Ignore this property completely. continue; } } else if (hasExplicitName) { // Specified HtmlAttributeNameAttribute.Name though property has no public setter. errorSink.OnError( SourceLocation.Zero, Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty( type.FullName, property.Name, typeof(HtmlAttributeNameAttribute).FullName, nameof(HtmlAttributeNameAttribute.Name)), length: 0); continue; } bool isInvalid; var indexerDescriptor = ToIndexerAttributeDescriptor( property, attributeNameAttribute, parentType: type, errorSink: errorSink, defaultPrefix: attributeName + "-", designTime: designTime, isInvalid: out isInvalid); if (indexerDescriptor != null && !ValidateTagHelperAttributeDescriptor(indexerDescriptor, type, errorSink)) { isInvalid = true; } if (isInvalid) { // The property type or HtmlAttributeNameAttribute.DictionaryAttributePrefix (or perhaps the // HTML-casing of the property name) is invalid. Ignore this property completely. continue; } if (mainDescriptor != null) { attributeDescriptors.Add(mainDescriptor); } if (indexerDescriptor != null) { indexerDescriptors.Add(indexerDescriptor); } } attributeDescriptors.AddRange(indexerDescriptors); return(attributeDescriptors); }
// Internal for testing. internal static bool ValidateTagHelperAttributeDescriptor( TagHelperAttributeDescriptor attributeDescriptor, ITypeInfo parentType, ErrorSink errorSink) { string nameOrPrefix; if (attributeDescriptor.IsIndexer) { nameOrPrefix = Resources.TagHelperDescriptorFactory_Prefix; } else if (string.IsNullOrEmpty(attributeDescriptor.Name)) { errorSink.OnError( SourceLocation.Zero, Resources.FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty( parentType.FullName, attributeDescriptor.PropertyName), length: 0); return false; } else { nameOrPrefix = Resources.TagHelperDescriptorFactory_Name; } return ValidateTagHelperAttributeNameOrPrefix( attributeDescriptor.Name, parentType, attributeDescriptor.PropertyName, errorSink, nameOrPrefix); }
private void RenderBufferedAttributeValue(TagHelperAttributeDescriptor attributeDescriptor) { // Pass complexValue: false because variable.ToString() replaces any original complexity in the expression. RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { RenderBufferedAttributeValueAccessor(writer); }, complexValue: false); }
private void RenderQuotedAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { writer.WriteStringLiteral(value); }, complexValue: false); }