From de4cafa8cdbe357ca3c4a34b084e3cf09c0db231 Mon Sep 17 00:00:00 2001 From: Doug Bunting Date: Wed, 14 Jan 2015 00:18:33 -0800 Subject: [PATCH] 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 --- .../CSharp/CSharpTagHelperCodeRenderer.cs | 123 ++++++----- .../CSharpTagHelperAttributeValueVisitor.cs | 179 ++++++++++++++++ .../Generator/TagHelperCodeGenerator.cs | 1 + .../Properties/RazorResources.Designer.cs | 196 ++++++------------ .../RazorResources.resx | 39 ++-- .../TagHelperAttributeValueCodeRenderer.cs | 16 +- .../Generator/CSharpTagHelperRenderingTest.cs | 42 ++-- ...TagHelperAttributeValueCodeRendererTest.cs | 5 +- ...TagHelpers.CustomAttributeCodeGenerator.cs | 4 + .../CS/Output/BasicTagHelpers.DesignTime.cs | 4 + .../CS/Output/BasicTagHelpers.cs | 4 + .../CS/Output/ComplexTagHelpers.DesignTime.cs | 70 ++++++- .../CS/Output/ComplexTagHelpers.cs | 175 +++++++++++++++- .../CS/Output/SingleTagHelper.DesignTime.cs | 6 +- .../CS/Output/SingleTagHelper.cs | 6 +- .../CS/Source/ComplexTagHelpers.cshtml | 13 ++ 16 files changed, 654 insertions(+), 229 deletions(-) create mode 100644 src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperAttributeValueVisitor.cs diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs index 0e1c3fd23d..e313abf3bb 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs @@ -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 valueRenderer) + Action valueRenderer, + bool complexValue) { - AttributeValueCodeRenderer.RenderAttributeValue(attributeDescriptor, _writer, _context, valueRenderer); + AttributeValueCodeRenderer.RenderAttributeValue( + attributeDescriptor, + _writer, + _context, + valueRenderer, + complexValue); } private void RenderBufferedAttributeValueAccessor(CSharpCodeWriter writer) diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperAttributeValueVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperAttributeValueVisitor.cs new file mode 100644 index 0000000000..ec6172aa8f --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperAttributeValueVisitor.cs @@ -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 +{ + /// + /// that writes code for a non- tag helper + /// bound attribute value. + /// + /// + /// Since attribute value is not written out as HTML, does not emit instrumentation. Further this + /// writes identical code at design- and runtime. + /// + public class CSharpTagHelperAttributeValueVisitor : CodeVisitor + { + private string _attributeTypeName; + private bool _firstChild; + + /// + /// Initializes a new instance of the class. + /// + /// The used to write code. + /// + /// A instance that contains information about the current code generation + /// process. + /// + /// + /// Full name of the property for which this + /// is writing the value. + /// + public CSharpTagHelperAttributeValueVisitor( + CSharpCodeWriter writer, + CodeBuilderContext context, + string attributeTypeName) + : base(writer, context) + { + _attributeTypeName = attributeTypeName; + } + + /// + /// Writes code for the given . + /// + /// The to render. + /// + /// Tracks code mappings for all children while writing. + /// + 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); + } + } + + /// + /// Writes code for the given . + /// + /// The to render. + protected override void Visit(ExpressionBlockChunk chunk) + { + Accept(chunk.Children); + } + + /// + /// Writes code for the given . + /// + /// The to render. + protected override void Visit(ExpressionChunk chunk) + { + RenderCode(chunk.Code, (Span)chunk.Association); + } + + /// + /// Writes code for the given . + /// + /// The to render. + protected override void Visit(LiteralChunk chunk) + { + RenderCode(chunk.Text, (Span)chunk.Association); + } + + /// + /// Writes code for the given . + /// + /// The to render. + /// + /// Allowed to support future C# extensions. Likely "~/..." will lead to a C# compilation error but that is up + /// to the compiler. + /// + protected override void Visit(ResolveUrlChunk chunk) + { + RenderCode(chunk.Url, (Span)chunk.Association); + } + + /// + /// Writes code for the given . + /// + /// The to render. + /// + /// Unconditionally adds a to inform user of unexpected @section directive. + /// + protected override void Visit(SectionChunk chunk) + { + Context.ErrorSink.OnError( + chunk.Association.Start, + RazorResources.FormatTagHelpers_Directives_NotSupported_InAttributes( + SyntaxConstants.CSharp.SectionKeyword), + chunk.Association.Length); + } + + /// + /// Writes code for the given . + /// + /// The to render. + /// + /// Unconditionally adds a to inform user of unexpected @layout directive. + /// + protected override void Visit(SetLayoutChunk chunk) + { + Context.ErrorSink.OnError( + chunk.Association.Start, + RazorResources.FormatTagHelpers_Directives_NotSupported_InAttributes( + SyntaxConstants.CSharp.LayoutKeyword), + chunk.Association.Length); + } + + /// + /// Writes code for the given . + /// + /// The to render. + /// + /// Unconditionally adds a to inform user of unexpected code block. + /// + protected override void Visit(StatementChunk chunk) + { + Context.ErrorSink.OnError( + chunk.Association.Start, + RazorResources.TagHelpers_CodeBlocks_NotSupported_InAttributes, + chunk.Association.Length); + } + + /// + /// Writes code for the given . + /// + /// The to render. + /// + /// Unconditionally adds a to inform user of unexpected template e.g. + /// @<p>paragraph@</p>. + /// + 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); + } + } + } +} diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs index 2a1fcf7593..d09b718051 100644 --- a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs @@ -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 }; diff --git a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs index fe382a5223..a238ee5343 100644 --- a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs +++ b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs @@ -390,38 +390,6 @@ namespace Microsoft.AspNet.Razor return GetString("ParseError_Unterminated_String_Literal"); } - /// - /// Unknown option: "{0}". - /// - internal static string ParseError_UnknownOption - { - get { return GetString("ParseError_UnknownOption"); } - } - - /// - /// Unknown option: "{0}". - /// - internal static string FormatParseError_UnknownOption(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_UnknownOption"), p0); - } - - /// - /// The "{0}" block was not terminated. All "{0}" statements must be terminated with a matching "{1}". - /// - internal static string ParseError_BlockNotTerminated - { - get { return GetString("ParseError_BlockNotTerminated"); } - } - - /// - /// The "{0}" block was not terminated. All "{0}" statements must be terminated with a matching "{1}". - /// - internal static string FormatParseError_BlockNotTerminated(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_BlockNotTerminated"), p0, p1); - } - /// /// @section Header { ... } /// @@ -454,22 +422,6 @@ namespace Microsoft.AspNet.Razor return GetString("ParseError_TextTagCannotContainAttributes"); } - /// - /// The "Imports" keyword must be followed by a namespace or a type alias on the same line. - /// - internal static string ParseError_NamespaceOrTypeAliasExpected - { - get { return GetString("ParseError_NamespaceOrTypeAliasExpected"); } - } - - /// - /// The "Imports" keyword must be followed by a namespace or a type alias on the same line. - /// - internal static string FormatParseError_NamespaceOrTypeAliasExpected() - { - return GetString("ParseError_NamespaceOrTypeAliasExpected"); - } - /// /// 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. /// @@ -998,22 +950,6 @@ namespace Microsoft.AspNet.Razor return string.Format(CultureInfo.CurrentCulture, GetString("TokenizerView_CannotPutBack"), p0, p1); } - /// - /// Unexpected "{0}" - /// - internal static string ParseError_Unexpected - { - get { return GetString("ParseError_Unexpected"); } - } - - /// - /// Unexpected "{0}" - /// - internal static string FormatParseError_Unexpected(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_Unexpected"), p0); - } - /// /// Unexpected "{" after "@" character. Once inside the body of a code block (@if {}, @{}, etc.) you do not need to use "@{" to switch to code. /// @@ -1142,38 +1078,6 @@ namespace Microsoft.AspNet.Razor return GetString("Language_Does_Not_Support_RazorComment"); } - /// - /// Missing value for session state directive. - /// - internal static string ParserEror_SessionDirectiveMissingValue - { - get { return GetString("ParserEror_SessionDirectiveMissingValue"); } - } - - /// - /// Missing value for session state directive. - /// - internal static string FormatParserEror_SessionDirectiveMissingValue() - { - return GetString("ParserEror_SessionDirectiveMissingValue"); - } - - /// - /// Cannot call CreateCodeWriter, a CodeWriter was not provided to the Create method - /// - internal static string CreateCodeWriter_NoCodeWriter - { - get { return GetString("CreateCodeWriter_NoCodeWriter"); } - } - - /// - /// Cannot call CreateCodeWriter, a CodeWriter was not provided to the Create method - /// - internal static string FormatCreateCodeWriter_NoCodeWriter() - { - return GetString("CreateCodeWriter_NoCodeWriter"); - } - /// /// [BG][{0}] Shutdown /// @@ -1478,22 +1382,6 @@ namespace Microsoft.AspNet.Razor return GetString("TagHelpers_TagHelperCodeGeneartorMustBeAssociatedWithATagHelperBlock"); } - /// - /// TagHelper attributes that do not expect strings must not have @ symbols within them. Found attribute '{0}' with an invalid value. - /// - internal static string TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols - { - get { return GetString("TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols"); } - } - - /// - /// TagHelper attributes that do not expect strings must not have @ symbols within them. Found attribute '{0}' with an invalid value. - /// - internal static string FormatTagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols"), p0); - } - /// /// Directive '{0}' must have a value. /// @@ -1542,22 +1430,6 @@ namespace Microsoft.AspNet.Razor return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper"), p0); } - /// - /// Missing '{0}' from '{1}' tag helper. - /// - internal static string TagHelpersParseTreeRewriter_MissingValueFromTagHelper - { - get { return GetString("TagHelpersParseTreeRewriter_MissingValueFromTagHelper"); } - } - - /// - /// Missing '{0}' from '{1}' tag helper. - /// - internal static string FormatTagHelpersParseTreeRewriter_MissingValueFromTagHelper(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_MissingValueFromTagHelper"), p0, p1); - } - /// /// Missing close angle for tag helper '{0}'. /// @@ -1590,6 +1462,74 @@ namespace Microsoft.AspNet.Razor return GetString("TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed"); } + /// + /// Non-string tag helper attribute values must not be empty. Add an expression to this attribute value. + /// + internal static string TagHelpers_AttributeExpressionRequired + { + get { return GetString("TagHelpers_AttributeExpressionRequired"); } + } + + /// + /// Non-string tag helper attribute values must not be empty. Add an expression to this attribute value. + /// + internal static string FormatTagHelpers_AttributeExpressionRequired() + { + return GetString("TagHelpers_AttributeExpressionRequired"); + } + + /// + /// 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. + /// + internal static string TagHelpers_CodeBlocks_NotSupported_InAttributes + { + get { return GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes"); } + } + + /// + /// 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. + /// + internal static string FormatTagHelpers_CodeBlocks_NotSupported_InAttributes() + { + return GetString("TagHelpers_CodeBlocks_NotSupported_InAttributes"); + } + + /// + /// @'{0}' directives must not appear in non-string tag helper attribute values. + /// + internal static string TagHelpers_Directives_NotSupported_InAttributes + { + get { return GetString("TagHelpers_Directives_NotSupported_InAttributes"); } + } + + /// + /// @'{0}' directives must not appear in non-string tag helper attribute values. + /// + internal static string FormatTagHelpers_Directives_NotSupported_InAttributes(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_Directives_NotSupported_InAttributes"), p0); + } + + /// + /// Inline markup blocks (e.g. @<p>content</p>) must not appear in non-string tag helper attribute values. + /// Expected a '{0}' attribute value, not a string. + /// + internal static string TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes + { + get { return GetString("TagHelpers_InlineMarkupBlocks_NotSupported_InAttributes"); } + } + + /// + /// Inline markup blocks (e.g. @<p>content</p>) must not appear in non-string tag helper attribute values. + /// Expected a '{0}' attribute value, not a string. + /// + 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); diff --git a/src/Microsoft.AspNet.Razor/RazorResources.resx b/src/Microsoft.AspNet.Razor/RazorResources.resx index 3f62c0eb90..748e14ef5d 100644 --- a/src/Microsoft.AspNet.Razor/RazorResources.resx +++ b/src/Microsoft.AspNet.Razor/RazorResources.resx @@ -206,12 +206,6 @@ Instead, wrap the contents of the block in "{{}}": 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. - - Unknown option: "{0}". - - - The "{0}" block was not terminated. All "{0}" statements must be terminated with a matching "{1}". - @section Header { ... } In CSHTML, the @section keyword is case-sensitive and lowercase (as with all C# keywords) @@ -219,9 +213,6 @@ Instead, wrap the contents of the block in "{{}}": "<text>" and "</text>" tags cannot contain attributes. - - The "Imports" keyword must be followed by a namespace or a type alias on the same line. - 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. @@ -321,9 +312,6 @@ Instead, wrap the contents of the block in "{{}}": 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} - - Unexpected "{0}" - Unexpected "{" after "@" character. Once inside the body of a code block (@if {}, @{}, etc.) you do not need to use "@{" to switch to code. @@ -348,13 +336,6 @@ Instead, wrap the contents of the block in "{{}}": 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 - - Missing value for session state directive. - - - Cannot call CreateCodeWriter, a CodeWriter was not provided to the Create method - 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 - [BG][{0}] Shutdown @@ -412,9 +393,6 @@ Instead, wrap the contents of the block in "{{}}": A TagHelperCodeGenerator must only be used with TagHelperBlocks. - - TagHelper attributes that do not expect strings must not have @ symbols within them. Found attribute '{0}' with an invalid value. - Directive '{0}' must have a value. @@ -424,13 +402,24 @@ Instead, wrap the contents of the block in "{{}}": Found a malformed '{0}' tag helper. Tag helpers must have a start and end tag or be self closing. - - Missing '{0}' from '{1}' tag helper. - Missing close angle for tag helper '{0}'. TagHelper attributes must be welformed. + + Non-string tag helper attribute values must not be empty. Add an expression to this attribute 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. + + + @'{0}' directives must not appear in non-string tag helper attribute values. + + + Inline markup blocks (e.g. @<p>content</p>) must not appear in non-string tag helper attribute values. + Expected a '{0}' attribute value, not a string. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeValueCodeRenderer.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeValueCodeRenderer.cs index 25e7f69f46..332583ce3a 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeValueCodeRenderer.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperAttributeValueCodeRenderer.cs @@ -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. /// - /// The to generate code for. + /// + /// The to generate code for. + /// /// The that's used to write code. /// A instance that contains information about /// the current code generation process. - /// that renders the raw value of the HTML attribute. + /// + /// that renders the raw value of the HTML attribute. + /// + /// + /// Indicates whether or not the source attribute value contains more than simple text. false for plain + /// C# expressions e.g. "PropertyName". true if the attribute value contain at least one in-line + /// Razor construct e.g. "@(@readonly)". + /// public virtual void RenderAttributeValue([NotNull] TagHelperAttributeDescriptor attributeDescriptor, [NotNull] CSharpCodeWriter writer, [NotNull] CodeBuilderContext context, - [NotNull] Action renderAttributeValue) + [NotNull] Action renderAttributeValue, + bool complexValue) { renderAttributeValue(writer); } diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs index 46c1d63f32..566a458e62 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs @@ -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), } } }; diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs index d94f405033..01f795831d 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs @@ -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 renderAttributeValue) + [NotNull] Action renderAttributeValue, + bool complexValue) { writer.Write("**From custom attribute code renderer**: "); - base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue); + base.RenderAttributeValue(attributeInfo, writer, context, renderAttributeValue, complexValue); } } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs index 9480b4d550..006b0e3410 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.CustomAttributeCodeGenerator.cs @@ -69,7 +69,11 @@ namespace TestOutput __InputTagHelper2 = CreateTagHelper(); __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()); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs index 2e64d06da7..9f057ded04 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.DesignTime.cs @@ -42,7 +42,11 @@ namespace TestOutput __InputTagHelper.Type = "checkbox"; __InputTagHelper2 = CreateTagHelper(); __InputTagHelper2.Type = __InputTagHelper.Type; +#line 7 "BasicTagHelpers.cshtml" __InputTagHelper2.Checked = true; + +#line default +#line hidden } #pragma warning restore 1998 } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs index 23c8d1cc14..5acb09e775 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/BasicTagHelpers.cs @@ -70,7 +70,11 @@ namespace TestOutput __InputTagHelper2 = CreateTagHelper(); __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()); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.DesignTime.cs index 28c9ac70b7..2dc8547ca5 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.DesignTime.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.DesignTime.cs @@ -83,7 +83,11 @@ __o = checkbox; __InputTagHelper.Type = string.Empty; __InputTagHelper2 = CreateTagHelper(); __InputTagHelper2.Type = __InputTagHelper.Type; - __InputTagHelper2.Checked = true; +#line 16 "ComplexTagHelpers.cshtml" + __InputTagHelper2.Checked = true; + +#line default +#line hidden __InputTagHelper = CreateTagHelper(); #line 17 "ComplexTagHelpers.cshtml" __o = true ? "checkbox" : "anything"; @@ -121,7 +125,71 @@ if(true) { #line default #line hidden + __PTagHelper = CreateTagHelper(); +#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(); + __InputTagHelper2 = CreateTagHelper(); +#line 23 "ComplexTagHelpers.cshtml" +__InputTagHelper2.Checked = @object; + +#line default +#line hidden + __PTagHelper = CreateTagHelper(); +#line 25 "ComplexTagHelpers.cshtml" +__PTagHelper.Age = -1970 + DateTimeOffset.Now.Year; + +#line default +#line hidden + __InputTagHelper = CreateTagHelper(); + __InputTagHelper2 = CreateTagHelper(); +#line 26 "ComplexTagHelpers.cshtml" +__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014; + +#line default +#line hidden + __PTagHelper = CreateTagHelper(); +#line 28 "ComplexTagHelpers.cshtml" +__PTagHelper.Age = DateTimeOffset.Now.Year - 1970; + +#line default +#line hidden + __InputTagHelper = CreateTagHelper(); + __InputTagHelper2 = CreateTagHelper(); +#line 29 "ComplexTagHelpers.cshtml" +__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014; + +#line default +#line hidden + __PTagHelper = CreateTagHelper(); +#line 31 "ComplexTagHelpers.cshtml" +__PTagHelper.Age = "My age is this long.".Length; + +#line default +#line hidden + __InputTagHelper = CreateTagHelper(); + __InputTagHelper2 = CreateTagHelper(); +#line 32 "ComplexTagHelpers.cshtml" +__InputTagHelper2.Checked = DateTimeOffset.Now.Year > 2014 ; + +#line default +#line hidden +#line 35 "ComplexTagHelpers.cshtml" } #line default diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.cs index 3b4087d2f4..799b71571c 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ComplexTagHelpers.cs @@ -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(); __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 \r\n"); + Instrumentation.BeginContext(666, 10, true); + WriteLiteral("\r\n "); + Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test"); + __PTagHelper = CreateTagHelper(); + __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(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper2 = CreateTagHelper(); + __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(); + __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(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper2 = CreateTagHelper(); + __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(); + __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(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper2 = CreateTagHelper(); + __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(); + __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(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper2 = CreateTagHelper(); + __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 \r\n"); + Instrumentation.EndContext(); +#line 35 "ComplexTagHelpers.cshtml" } #line default diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.DesignTime.cs index 16fb5c4ed4..b2797a754d 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.DesignTime.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.DesignTime.cs @@ -31,7 +31,11 @@ namespace TestOutput public override async Task ExecuteAsync() { __PTagHelper = CreateTagHelper(); - __PTagHelper.Age = 1337; +#line 3 "SingleTagHelper.cshtml" + __PTagHelper.Age = 1337; + +#line default +#line hidden } #pragma warning restore 1998 } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.cs index bf355d24bb..cb4fa34851 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/SingleTagHelper.cs @@ -29,7 +29,11 @@ namespace TestOutput __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test"); __PTagHelper = CreateTagHelper(); __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; diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ComplexTagHelpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ComplexTagHelpers.cshtml index 7ba3c6db48..2053f692d1 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ComplexTagHelpers.cshtml +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ComplexTagHelpers.cshtml @@ -18,5 +18,18 @@ }

+

+ @{ var @object = false;} + +

+

+ +

+

+ +

+

+ +

} \ No newline at end of file