Handle complex expressions for bound non-string tag helper attributes

- #129 and support aspnet/Mvc#1253
- add new `CSharpTagHelperAttributeValueVisitor` that writes the raw expression
- add tests of embedded `@(...)` and mix that with normal expressions
- add new resources for errors in `CSharpTagHelperAttributeValueVisitor`
 - write errors using `ParserErrorSink`
- update baselines to match new code generation

nits:
- cleanup long lines in `CSharpTagHelperCodeRenderer`
- remove a few unused resources
This commit is contained in:
Doug Bunting 2015-01-14 00:18:33 -08:00
parent 7b8126367c
commit de4cafa8cd
16 changed files with 654 additions and 229 deletions

View File

@ -5,8 +5,8 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
using Microsoft.AspNet.Razor.TagHelpers;
using Microsoft.AspNet.Razor.Text;
namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{
@ -230,48 +230,67 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
tagHelperVariableName,
attributeDescriptor.PropertyName);
_writer.WriteStartAssignment(valueAccessor);
// If we haven't recorded this attribute value before then we need to record its value.
if (!attributeValueRecorded)
{
// We only need to create attribute values once per HTML element (not once per tag helper).
// We're saving the value accessor so we can retrieve it later if there are more tag helpers that
// need the value.
// We're saving the value accessor so we can retrieve it later if there are more tag
// helpers that need the value.
htmlAttributeValues.Add(attributeDescriptor.Name, valueAccessor);
if (bufferableAttribute)
{
// 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.
_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. someAttribute="Time: @DateTime.Now"
// The value contains more than plain text e.g.
// stringAttribute ="Time: @DateTime.Now"
RenderBufferedAttributeValue(attributeDescriptor);
}
_writer.WriteLine(";");
}
else
{
// TODO: Make complex types in non-bufferable attributes work in
// https://github.com/aspnet/Razor/issues/129
if (!isPlainTextValue)
// 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(";");
return;
}
// We aren't a bufferable attribute which means we have no Razor code in our value.
// Therefore we can just use the "textValue" as the attribute value.
RenderRawAttributeValue(textValue, attributeValueChunk.Start, attributeDescriptor);
}
// End the assignment to the attribute.
_writer.WriteLine(";");
// Execution contexts are a runtime feature.
if (_designTimeMode)
{
@ -279,20 +298,23 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
}
// We need to inform the context of the attribute value.
_writer.WriteStartInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName);
_writer.WriteStringLiteral(attributeDescriptor.Name)
.WriteParameterSeparator()
.Write(valueAccessor)
.WriteEndMethodInvocation();
_writer
.WriteStartInstanceMethodInvocation(
ExecutionContextVariableName,
_tagHelperContext.ExecutionContextAddTagHelperAttributeMethodName)
.WriteStringLiteral(attributeDescriptor.Name)
.WriteParameterSeparator()
.Write(valueAccessor)
.WriteEndMethodInvocation();
}
else
{
// The attribute value has already been recorded, lets retrieve it from the stored value accessors.
_writer.Write(htmlAttributeValues[attributeDescriptor.Name])
.WriteLine(";");
// The attribute value has already been recorded, lets retrieve it from the stored value
// accessors.
_writer
.WriteStartAssignment(valueAccessor)
.Write(htmlAttributeValues[attributeDescriptor.Name])
.WriteLine(";");
}
}
}
@ -414,34 +436,30 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
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 RenderRawAttributeValue(string value,
SourceLocation documentLocation,
TagHelperAttributeDescriptor attributeDescriptor)
private void RenderRawAttributeValue(
Chunk attributeValueChunk,
TagHelperAttributeDescriptor attributeDescriptor,
bool isPlainTextValue)
{
RenderAttributeValue(
attributeDescriptor,
valueRenderer: (writer) =>
{
if (_context.Host.DesignTimeMode)
{
using (new CSharpLineMappingWriter(writer, documentLocation, value.Length))
{
writer.Write(value);
}
}
else
{
writer.Write(value);
}
});
var visitor =
new CSharpTagHelperAttributeValueVisitor(writer, _context, attributeDescriptor.TypeName);
visitor.Accept(attributeValueChunk);
},
complexValue: !isPlainTextValue);
}
private void RenderQuotedAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor)
@ -451,7 +469,8 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
valueRenderer: (writer) =>
{
writer.WriteStringLiteral(value);
});
},
complexValue: false);
}
private void BuildBufferedWritingScope(Chunk htmlAttributeChunk)
@ -502,9 +521,15 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
}
private void RenderAttributeValue(TagHelperAttributeDescriptor attributeDescriptor,
Action<CSharpCodeWriter> valueRenderer)
Action<CSharpCodeWriter> valueRenderer,
bool complexValue)
{
AttributeValueCodeRenderer.RenderAttributeValue(attributeDescriptor, _writer, _context, valueRenderer);
AttributeValueCodeRenderer.RenderAttributeValue(
attributeDescriptor,
_writer,
_context,
valueRenderer,
complexValue);
}
private void RenderBufferedAttributeValueAccessor(CSharpCodeWriter writer)

View File

@ -0,0 +1,179 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNet.Razor.Parser;
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp
{
/// <summary>
/// <see cref="CodeVisitor{CSharpCodeWriter}"/> that writes code for a non-<see langword="string"/> tag helper
/// bound attribute value.
/// </summary>
/// <remarks>
/// Since attribute value is not written out as HTML, does not emit instrumentation. Further this
/// <see cref="CodeVisitor{CSharpCodeWriter}"/> writes identical code at design- and runtime.
/// </remarks>
public class CSharpTagHelperAttributeValueVisitor : CodeVisitor<CSharpCodeWriter>
{
private string _attributeTypeName;
private bool _firstChild;
/// <summary>
/// Initializes a new instance of the <see cref="CSharpTagHelperAttributeValueVisitor"/> class.
/// </summary>
/// <param name="writer">The <see cref="CSharpCodeWriter"/> used to write code.</param>
/// <param name="context">
/// A <see cref="CodeBuilderContext"/> instance that contains information about the current code generation
/// process.
/// </param>
/// <param name="attributeTypeName">
/// Full name of the property <see cref="System.Type"/> for which this
/// <see cref="CSharpTagHelperAttributeValueVisitor"/> is writing the value.
/// </param>
public CSharpTagHelperAttributeValueVisitor(
CSharpCodeWriter writer,
CodeBuilderContext context,
string attributeTypeName)
: base(writer, context)
{
_attributeTypeName = attributeTypeName;
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ChunkBlock"/> to render.</param>
/// <remarks>
/// Tracks code mappings for all children while writing.
/// </remarks>
protected override void Visit(ChunkBlock chunk)
{
// Line mappings are captured in RenderCode(), not this method.
_firstChild = true;
Accept(chunk.Children);
if (_firstChild)
{
// Attribute value was empty.
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.TagHelpers_AttributeExpressionRequired,
chunk.Association.Length);
}
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ExpressionBlockChunk"/> to render.</param>
protected override void Visit(ExpressionBlockChunk chunk)
{
Accept(chunk.Children);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ExpressionChunk"/> to render.</param>
protected override void Visit(ExpressionChunk chunk)
{
RenderCode(chunk.Code, (Span)chunk.Association);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="LiteralChunk"/> to render.</param>
protected override void Visit(LiteralChunk chunk)
{
RenderCode(chunk.Text, (Span)chunk.Association);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="ResolveUrlChunk"/> to render.</param>
/// <remarks>
/// Allowed to support future C# extensions. Likely "~/..." will lead to a C# compilation error but that is up
/// to the compiler.
/// </remarks>
protected override void Visit(ResolveUrlChunk chunk)
{
RenderCode(chunk.Url, (Span)chunk.Association);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="SectionChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected <c>@section</c> directive.
/// </remarks>
protected override void Visit(SectionChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.FormatTagHelpers_Directives_NotSupported_InAttributes(
SyntaxConstants.CSharp.SectionKeyword),
chunk.Association.Length);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="SetLayoutChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected <c>@layout</c> directive.
/// </remarks>
protected override void Visit(SetLayoutChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.FormatTagHelpers_Directives_NotSupported_InAttributes(
SyntaxConstants.CSharp.LayoutKeyword),
chunk.Association.Length);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="StatementChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected code block.
/// </remarks>
protected override void Visit(StatementChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.TagHelpers_CodeBlocks_NotSupported_InAttributes,
chunk.Association.Length);
}
/// <summary>
/// Writes code for the given <paramref name="chunk"/>.
/// </summary>
/// <param name="chunk">The <see cref="TemplateChunk"/> to render.</param>
/// <remarks>
/// Unconditionally adds a <see cref="RazorError"/> to inform user of unexpected template e.g.
/// <c>@&lt;p&gt;paragraph@&lt;/p&gt;</c>.
/// </remarks>
protected override void Visit(TemplateChunk chunk)
{
Context.ErrorSink.OnError(
chunk.Association.Start,
RazorResources.FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(_attributeTypeName),
chunk.Association.Length);
}
// Tracks the code mapping and writes code for a leaf node in the attribute value Chunk tree.
private void RenderCode(string code, Span association)
{
_firstChild = false;
using (new CSharpLineMappingWriter(Writer, association.Start, code.Length))
{
Writer.Write(code);
}
}
}
}

View File

@ -66,6 +66,7 @@ namespace Microsoft.AspNet.Razor.Generator
attributes[attribute.Key] = new ChunkBlock
{
Association = first?.Association,
Children = chunks,
Start = first == null ? SourceLocation.Zero : first.Start
};

View File

@ -390,38 +390,6 @@ namespace Microsoft.AspNet.Razor
return GetString("ParseError_Unterminated_String_Literal");
}
/// <summary>
/// Unknown option: "{0}".
/// </summary>
internal static string ParseError_UnknownOption
{
get { return GetString("ParseError_UnknownOption"); }
}
/// <summary>
/// Unknown option: "{0}".
/// </summary>
internal static string FormatParseError_UnknownOption(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_UnknownOption"), p0);
}
/// <summary>
/// The "{0}" block was not terminated. All "{0}" statements must be terminated with a matching "{1}".
/// </summary>
internal static string ParseError_BlockNotTerminated
{
get { return GetString("ParseError_BlockNotTerminated"); }
}
/// <summary>
/// The "{0}" block was not terminated. All "{0}" statements must be terminated with a matching "{1}".
/// </summary>
internal static string FormatParseError_BlockNotTerminated(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_BlockNotTerminated"), p0, p1);
}
/// <summary>
/// @section Header { ... }
/// </summary>
@ -454,22 +422,6 @@ namespace Microsoft.AspNet.Razor
return GetString("ParseError_TextTagCannotContainAttributes");
}
/// <summary>
/// The "Imports" keyword must be followed by a namespace or a type alias on the same line.
/// </summary>
internal static string ParseError_NamespaceOrTypeAliasExpected
{
get { return GetString("ParseError_NamespaceOrTypeAliasExpected"); }
}
/// <summary>
/// The "Imports" keyword must be followed by a namespace or a type alias on the same line.
/// </summary>
internal static string FormatParseError_NamespaceOrTypeAliasExpected()
{
return GetString("ParseError_NamespaceOrTypeAliasExpected");
}
/// <summary>
/// A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.
/// </summary>
@ -998,22 +950,6 @@ namespace Microsoft.AspNet.Razor
return string.Format(CultureInfo.CurrentCulture, GetString("TokenizerView_CannotPutBack"), p0, p1);
}
/// <summary>
/// Unexpected "{0}"
/// </summary>
internal static string ParseError_Unexpected
{
get { return GetString("ParseError_Unexpected"); }
}
/// <summary>
/// Unexpected "{0}"
/// </summary>
internal static string FormatParseError_Unexpected(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Unexpected"), p0);
}
/// <summary>
/// Unexpected "{" after "@" character. Once inside the body of a code block (@if {}, @{}, etc.) you do not need to use "@{" to switch to code.
/// </summary>
@ -1142,38 +1078,6 @@ namespace Microsoft.AspNet.Razor
return GetString("Language_Does_Not_Support_RazorComment");
}
/// <summary>
/// Missing value for session state directive.
/// </summary>
internal static string ParserEror_SessionDirectiveMissingValue
{
get { return GetString("ParserEror_SessionDirectiveMissingValue"); }
}
/// <summary>
/// Missing value for session state directive.
/// </summary>
internal static string FormatParserEror_SessionDirectiveMissingValue()
{
return GetString("ParserEror_SessionDirectiveMissingValue");
}
/// <summary>
/// Cannot call CreateCodeWriter, a CodeWriter was not provided to the Create method
/// </summary>
internal static string CreateCodeWriter_NoCodeWriter
{
get { return GetString("CreateCodeWriter_NoCodeWriter"); }
}
/// <summary>
/// Cannot call CreateCodeWriter, a CodeWriter was not provided to the Create method
/// </summary>
internal static string FormatCreateCodeWriter_NoCodeWriter()
{
return GetString("CreateCodeWriter_NoCodeWriter");
}
/// <summary>
/// [BG][{0}] Shutdown
/// </summary>
@ -1478,22 +1382,6 @@ namespace Microsoft.AspNet.Razor
return GetString("TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock");
}
/// <summary>
/// TagHelper attributes that do not expect strings must not have @ symbols within them. Found attribute '{0}' with an invalid value.
/// </summary>
internal static string TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols
{
get { return GetString("TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols"); }
}
/// <summary>
/// TagHelper attributes that do not expect strings must not have @ symbols within them. Found attribute '{0}' with an invalid value.
/// </summary>
internal static string FormatTagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols"), p0);
}
/// <summary>
/// Directive '{0}' must have a value.
/// </summary>
@ -1542,22 +1430,6 @@ namespace Microsoft.AspNet.Razor
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper"), p0);
}
/// <summary>
/// Missing '{0}' from '{1}' tag helper.
/// </summary>
internal static string TagHelpersParseTreeRewriter_MissingValueFromTagHelper
{
get { return GetString("TagHelpersParseTreeRewriter_MissingValueFromTagHelper"); }
}
/// <summary>
/// Missing '{0}' from '{1}' tag helper.
/// </summary>
internal static string FormatTagHelpersParseTreeRewriter_MissingValueFromTagHelper(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_MissingValueFromTagHelper"), p0, p1);
}
/// <summary>
/// Missing close angle for tag helper '{0}'.
/// </summary>
@ -1590,6 +1462,74 @@ namespace Microsoft.AspNet.Razor
return GetString("TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed");
}
/// <summary>
/// Non-string tag helper attribute values must not be empty. Add an expression to this attribute value.
/// </summary>
internal static string TagHelpers_AttributeExpressionRequired
{
get { return GetString("TagHelpers_AttributeExpressionRequired"); }
}
/// <summary>
/// Non-string tag helper attribute values must not be empty. Add an expression to this attribute value.
/// </summary>
internal static string FormatTagHelpers_AttributeExpressionRequired()
{
return GetString("TagHelpers_AttributeExpressionRequired");
}
/// <summary>
/// Code blocks (e.g. @{{var variable = 23;}}) must not appear in non-string tag helper attribute values.
/// Already in an expression (code) context. If necessary an explicit expression (e.g. @(@readonly)) may be used.
/// </summary>
internal static string TagHelpers_CodeBlocks_NotSupported_InAttributes
{
get { return GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes"); }
}
/// <summary>
/// Code blocks (e.g. @{{var variable = 23;}}) must not appear in non-string tag helper attribute values.
/// Already in an expression (code) context. If necessary an explicit expression (e.g. @(@readonly)) may be used.
/// </summary>
internal static string FormatTagHelpers_CodeBlocks_NotSupported_InAttributes()
{
return GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes");
}
/// <summary>
/// @'{0}' directives must not appear in non-string tag helper attribute values.
/// </summary>
internal static string TagHelpers_Directives_NotSupported_InAttributes
{
get { return GetString("TagHelpers_Directives_NotSupported_InAttributes"); }
}
/// <summary>
/// @'{0}' directives must not appear in non-string tag helper attribute values.
/// </summary>
internal static string FormatTagHelpers_Directives_NotSupported_InAttributes(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_Directives_NotSupported_InAttributes"), p0);
}
/// <summary>
/// Inline markup blocks (e.g. @&lt;p&gt;content&lt;/p&gt;) must not appear in non-string tag helper attribute values.
/// Expected a '{0}' attribute value, not a string.
/// </summary>
internal static string TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes
{
get { return GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes"); }
}
/// <summary>
/// Inline markup blocks (e.g. @&lt;p&gt;content&lt;/p&gt;) must not appear in non-string tag helper attribute values.
/// Expected a '{0}' attribute value, not a string.
/// </summary>
internal static string FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes"), p0);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -206,12 +206,6 @@ Instead, wrap the contents of the block in "{{}}":
<data name="ParseError_Unterminated_String_Literal" xml:space="preserve">
<value>Unterminated string literal. Strings that start with a quotation mark (") must be terminated before the end of the line. However, strings that start with @ and a quotation mark (@") can span multiple lines.</value>
</data>
<data name="ParseError_UnknownOption" xml:space="preserve">
<value>Unknown option: "{0}".</value>
</data>
<data name="ParseError_BlockNotTerminated" xml:space="preserve">
<value>The "{0}" block was not terminated. All "{0}" statements must be terminated with a matching "{1}".</value>
</data>
<data name="SectionExample_CS" xml:space="preserve">
<value>@section Header { ... }</value>
<comment>In CSHTML, the @section keyword is case-sensitive and lowercase (as with all C# keywords)</comment>
@ -219,9 +213,6 @@ Instead, wrap the contents of the block in "{{}}":
<data name="ParseError_TextTagCannotContainAttributes" xml:space="preserve">
<value>"&lt;text&gt;" and "&lt;/text&gt;" tags cannot contain attributes.</value>
</data>
<data name="ParseError_NamespaceOrTypeAliasExpected" xml:space="preserve">
<value>The "Imports" keyword must be followed by a namespace or a type alias on the same line.</value>
</data>
<data name="ParseError_Unexpected_WhiteSpace_At_Start_Of_CodeBlock_CS" xml:space="preserve">
<value>A space or line break was encountered after the "@" character. Only valid identifiers, keywords, comments, "(" and "{" are valid at the start of a code block and they must occur immediately following "@" with no space in between.</value>
</data>
@ -321,9 +312,6 @@ Instead, wrap the contents of the block in "{{}}":
<data name="TokenizerView_CannotPutBack" xml:space="preserve">
<value>In order to put a symbol back, it must have been the symbol which ended at the current position. The specified symbol ends at {0}, but the current position is {1}</value>
</data>
<data name="ParseError_Unexpected" xml:space="preserve">
<value>Unexpected "{0}"</value>
</data>
<data name="ParseError_Unexpected_Nested_CodeBlock" xml:space="preserve">
<value>Unexpected "{" after "@" character. Once inside the body of a code block (@if {}, @{}, etc.) you do not need to use "@{" to switch to code.</value>
</data>
@ -348,13 +336,6 @@ Instead, wrap the contents of the block in "{{}}":
<data name="Language_Does_Not_Support_RazorComment" xml:space="preserve">
<value>Cannot use built-in RazorComment handler, language characteristics does not define the CommentStart, CommentStar and CommentBody known symbol types or parser does not override TokenizerBackedParser.OutputSpanBeforeRazorComment</value>
</data>
<data name="ParserEror_SessionDirectiveMissingValue" xml:space="preserve">
<value>Missing value for session state directive.</value>
</data>
<data name="CreateCodeWriter_NoCodeWriter" xml:space="preserve">
<value>Cannot call CreateCodeWriter, a CodeWriter was not provided to the Create method</value>
<comment>This error should not be seen by users, it should only appear to internal developers, but I'm putting it in resources just in case</comment>
</data>
<data name="Trace_BackgroundThreadShutdown" xml:space="preserve">
<value>[BG][{0}] Shutdown</value>
</data>
@ -412,9 +393,6 @@ Instead, wrap the contents of the block in "{{}}":
<data name="TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock" xml:space="preserve">
<value>A TagHelperCodeGenerator must only be used with TagHelperBlocks.</value>
</data>
<data name="TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols" xml:space="preserve">
<value>TagHelper attributes that do not expect strings must not have @ symbols within them. Found attribute '{0}' with an invalid value.</value>
</data>
<data name="ParseError_DirectiveMustHaveValue" xml:space="preserve">
<value>Directive '{0}' must have a value.</value>
</data>
@ -424,13 +402,24 @@ Instead, wrap the contents of the block in "{{}}":
<data name="TagHelpersParseTreeRewriter_FoundMalformedTagHelper" xml:space="preserve">
<value>Found a malformed '{0}' tag helper. Tag helpers must have a start and end tag or be self closing.</value>
</data>
<data name="TagHelpersParseTreeRewriter_MissingValueFromTagHelper" xml:space="preserve">
<value>Missing '{0}' from '{1}' tag helper.</value>
</data>
<data name="TagHelpersParseTreeRewriter_MissingCloseAngle" xml:space="preserve">
<value>Missing close angle for tag helper '{0}'.</value>
</data>
<data name="TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed" xml:space="preserve">
<value>TagHelper attributes must be welformed.</value>
</data>
<data name="TagHelpers_AttributeExpressionRequired" xml:space="preserve">
<value>Non-string tag helper attribute values must not be empty. Add an expression to this attribute value.</value>
</data>
<data name="TagHelpers_CodeBlocks_NotSupported_InAttributes" xml:space="preserve">
<value>Code blocks (e.g. @{{var variable = 23;}}) must not appear in non-string tag helper attribute values.
Already in an expression (code) context. If necessary an explicit expression (e.g. @(@readonly)) may be used.</value>
</data>
<data name="TagHelpers_Directives_NotSupported_InAttributes" xml:space="preserve">
<value>@'{0}' directives must not appear in non-string tag helper attribute values.</value>
</data>
<data name="TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes" xml:space="preserve">
<value>Inline markup blocks (e.g. @&lt;p&gt;content&lt;/p&gt;) must not appear in non-string tag helper attribute values.
Expected a '{0}' attribute value, not a string.</value>
</data>
</root>

View File

@ -16,15 +16,25 @@ namespace Microsoft.AspNet.Razor.TagHelpers
/// 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="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="CodeGeneratorContext"/> 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="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] CodeBuilderContext context,
[NotNull] Action<CSharpCodeWriter> renderAttributeValue)
[NotNull] Action<CSharpCodeWriter> renderAttributeValue,
bool complexValue)
{
renderAttributeValue(writer);
}

View File

@ -177,10 +177,9 @@ namespace Microsoft.AspNet.Razor.Test.Generator
contentLength: 11),
BuildLineMapping(documentAbsoluteIndex: 57,
documentLineIndex: 2,
documentCharacterOffsetIndex: 28,
generatedAbsoluteIndex: 927,
generatedLineIndex: 33,
generatedCharacterOffsetIndex: 31,
generatedAbsoluteIndex: 958,
generatedLineIndex: 34,
characterOffsetIndex: 28,
contentLength: 4)
}
},
@ -198,10 +197,9 @@ namespace Microsoft.AspNet.Razor.Test.Generator
contentLength: 11),
BuildLineMapping(documentAbsoluteIndex: 189,
documentLineIndex: 6,
documentCharacterOffsetIndex: 40,
generatedAbsoluteIndex: 1599,
generatedLineIndex: 44,
generatedCharacterOffsetIndex: 40,
generatedAbsoluteIndex: 1633,
generatedLineIndex: 45,
characterOffsetIndex: 40,
contentLength: 4)
}
},
@ -232,13 +230,27 @@ namespace Microsoft.AspNet.Razor.Test.Generator
BuildLineMapping(218, 9, 13, 1356, 56, 12, 27),
BuildLineMapping(346, 12, 1754, 68, 0, 48),
BuildLineMapping(440, 15, 46, 2004, 78, 6, 8),
BuildLineMapping(457, 15, 63, 2267, 85, 40, 4),
BuildLineMapping(501, 16, 31, 2384, 88, 6, 30),
BuildLineMapping(568, 17, 30, 2733, 97, 0, 10),
BuildLineMapping(601, 17, 63, 2815, 103, 0, 8),
BuildLineMapping(632, 17, 94, 2895, 109, 0, 1),
BuildLineMapping(639, 18, 3149, 118, 0, 15),
BuildLineMapping(680, 21, 3234, 124, 0, 1)
BuildLineMapping(457, 15, 2327, 86, 63, 4),
BuildLineMapping(501, 16, 31, 2475, 92, 6, 30),
BuildLineMapping(568, 17, 30, 2824, 101, 0, 10),
BuildLineMapping(601, 17, 63, 2906, 107, 0, 8),
BuildLineMapping(632, 17, 94, 2986, 113, 0, 1),
BuildLineMapping(639, 18, 3240, 122, 0, 15),
BuildLineMapping(685, 20, 17, 3403, 129, 19, 23),
BuildLineMapping(708, 20, 40, 3426, 129, 42, 7),
BuildLineMapping(719, 21, 3504, 134, 0, 12),
BuildLineMapping(733, 21, 3602, 140, 14, 21),
BuildLineMapping(787, 22, 30, 3859, 148, 28, 7),
BuildLineMapping(831, 24, 16, 4015, 154, 19, 8),
BuildLineMapping(840, 24, 25, 4023, 154, 27, 23),
BuildLineMapping(897, 25, 30, 4281, 161, 28, 30),
BuildLineMapping(964, 27, 16, 4460, 167, 19, 30),
BuildLineMapping(1026, 28, 4725, 174, 28, 30),
BuildLineMapping(1094, 30, 18, 4904, 180, 19, 29),
BuildLineMapping(1156, 31, 5168, 187, 28, 3),
BuildLineMapping(1161, 31, 33, 5171, 187, 31, 27),
BuildLineMapping(1189, 31, 61, 5198, 187, 58, 10),
BuildLineMapping(1231, 34, 5279, 192, 0, 1),
}
}
};

View File

@ -88,11 +88,12 @@ namespace Microsoft.AspNet.Razor.Test.Generator
public override void RenderAttributeValue([NotNull] TagHelperAttributeDescriptor attributeInfo,
[NotNull] CSharpCodeWriter writer,
[NotNull] CodeBuilderContext context,
[NotNull] Action<CSharpCodeWriter> renderAttributeValue)
[NotNull] Action<CSharpCodeWriter> renderAttributeValue,
bool complexValue)
{
writer.Write("**From custom attribute code renderer**: ");
base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue);
base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue, complexValue);
}
}

View File

@ -69,7 +69,11 @@ namespace TestOutput
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 7 "BasicTagHelpers.cshtml"
__InputTagHelper2.Checked = **From custom attribute code renderer**: true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());

View File

@ -42,7 +42,11 @@ namespace TestOutput
__InputTagHelper.Type = "checkbox";
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 7 "BasicTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
}
#pragma warning restore 1998
}

View File

@ -70,7 +70,11 @@ namespace TestOutput
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
#line 7 "BasicTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());

View File

@ -83,7 +83,11 @@ __o = checkbox;
__InputTagHelper.Type = string.Empty;
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__InputTagHelper2.Type = __InputTagHelper.Type;
__InputTagHelper2.Checked = true;
#line 16 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__InputTagHelper = CreateTagHelper<InputTagHelper>();
#line 17 "ComplexTagHelpers.cshtml"
__o = true ? "checkbox" : "anything";
@ -121,7 +125,71 @@ if(true) {
#line default
#line hidden
__PTagHelper = CreateTagHelper<PTagHelper>();
#line 21 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = DateTimeOffset.Now.Year - 1970;
#line default
#line hidden
#line 22 "ComplexTagHelpers.cshtml"
#line default
#line hidden
#line 22 "ComplexTagHelpers.cshtml"
var @object = false;
#line default
#line hidden
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
#line 23 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = @object;
#line default
#line hidden
__PTagHelper = CreateTagHelper<PTagHelper>();
#line 25 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = -1970 + DateTimeOffset.Now.Year;
#line default
#line hidden
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
#line 26 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014;
#line default
#line hidden
__PTagHelper = CreateTagHelper<PTagHelper>();
#line 28 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = DateTimeOffset.Now.Year - 1970;
#line default
#line hidden
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
#line 29 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014;
#line default
#line hidden
__PTagHelper = CreateTagHelper<PTagHelper>();
#line 31 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = "My age is this long.".Length;
#line default
#line hidden
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
#line 32 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014 ;
#line default
#line hidden
#line 35 "ComplexTagHelpers.cshtml"
}
#line default

View File

@ -1,4 +1,4 @@
#pragma checksum "ComplexTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "cce31144cd1c3c35d241b49e41c4fc04ff044565"
#pragma checksum "ComplexTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "7158abc69d261099393d49d82670ec64a94d9770"
namespace TestOutput
{
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
@ -134,7 +134,11 @@ Write(checkbox);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
__InputTagHelper2.Type = __InputTagHelper.Type;
__InputTagHelper2.Checked = true;
#line 16 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = true;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
@ -215,10 +219,173 @@ if(true) {
Instrumentation.EndContext();
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(666, 14, true);
WriteLiteral("\r\n </div>\r\n");
Instrumentation.BeginContext(666, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
#line 21 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = DateTimeOffset.Now.Year - 1970;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
Instrumentation.BeginContext(717, 2, true);
WriteLiteral("\r\n");
Instrumentation.EndContext();
#line 22 "ComplexTagHelpers.cshtml"
#line default
#line hidden
#line 22 "ComplexTagHelpers.cshtml"
var @object = false;
#line default
#line hidden
Instrumentation.BeginContext(755, 14, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test");
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
#line 23 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = @object;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(799, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(813, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
#line 25 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = -1970 + DateTimeOffset.Now.Year;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
Instrumentation.BeginContext(865, 14, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test");
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
#line 26 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(932, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(946, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
#line 28 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = DateTimeOffset.Now.Year - 1970;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
Instrumentation.BeginContext(996, 14, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test");
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
#line 29 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(1060, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(1074, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
#line 31 "ComplexTagHelpers.cshtml"
__PTagHelper.Age = "My age is this long.".Length;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
Instrumentation.BeginContext(1126, 14, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test");
__InputTagHelper = CreateTagHelper<InputTagHelper>();
__tagHelperExecutionContext.Add(__InputTagHelper);
__InputTagHelper2 = CreateTagHelper<InputTagHelper2>();
__tagHelperExecutionContext.Add(__InputTagHelper2);
#line 32 "ComplexTagHelpers.cshtml"
__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014 ;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked);
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;
WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag());
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(1203, 10, true);
WriteLiteral("\r\n ");
Instrumentation.EndContext();
WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag());
__tagHelperExecutionContext = __tagHelperScopeManager.End();
Instrumentation.BeginContext(1217, 14, true);
WriteLiteral("\r\n </div>\r\n");
Instrumentation.EndContext();
#line 35 "ComplexTagHelpers.cshtml"
}
#line default

View File

@ -31,7 +31,11 @@ namespace TestOutput
public override async Task ExecuteAsync()
{
__PTagHelper = CreateTagHelper<PTagHelper>();
__PTagHelper.Age = 1337;
#line 3 "SingleTagHelper.cshtml"
__PTagHelper.Age = 1337;
#line default
#line hidden
}
#pragma warning restore 1998
}

View File

@ -29,7 +29,11 @@ namespace TestOutput
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test");
__PTagHelper = CreateTagHelper<PTagHelper>();
__tagHelperExecutionContext.Add(__PTagHelper);
__PTagHelper.Age = 1337;
#line 3 "SingleTagHelper.cshtml"
__PTagHelper.Age = 1337;
#line default
#line hidden
__tagHelperExecutionContext.AddTagHelperAttribute("age", __PTagHelper.Age);
__tagHelperExecutionContext.AddHtmlAttribute("class", "Hello World");
__tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result;

View File

@ -18,5 +18,18 @@
<input type='@if(true) { <text>checkbox</text> } else { <text>anything</text> }' />
}
</p>
<p age="@DateTimeOffset.Now.Year - 1970">
@{ var @object = false;}
<input checked="@(@object)" />
</p>
<p age="-1970 + @DateTimeOffset.Now.Year">
<input checked="@(DateTimeOffset.Now.Year > 2014)" />
</p>
<p age="DateTimeOffset.Now.Year - 1970">
<input checked="DateTimeOffset.Now.Year > 2014" />
</p>
<p age="@("My age is this long.".Length)">
<input checked=" @( DateTimeOffset.Now.Year ) > 2014 " />
</p>
</div>
}