diff --git a/build.cmd b/build.cmd index 86ca5bbbf1..220a1ff561 100644 --- a/build.cmd +++ b/build.cmd @@ -19,10 +19,10 @@ IF EXIST packages\KoreBuild goto run .nuget\NuGet.exe install KoreBuild -ExcludeVersion -o packages -nocache -pre .nuget\NuGet.exe install Sake -version 0.2 -o packages -ExcludeVersion -IF "%SKIP_KRE_INSTALL%"=="1" goto run -CALL packages\KoreBuild\build\kvm upgrade -runtime CLR -x86 -CALL packages\KoreBuild\build\kvm install default -runtime CoreCLR -x86 +IF "%SKIP_DOTNET_INSTALL%"=="1" goto run +CALL packages\KoreBuild\build\dotnetsdk upgrade -runtime CLR -x86 +CALL packages\KoreBuild\build\dotnetsdk install default -runtime CoreCLR -x86 :run -CALL packages\KoreBuild\build\kvm use default -runtime CLR -x86 +CALL packages\KoreBuild\build\dotnetsdk use default -runtime CLR -x86 packages\Sake\tools\Sake.exe -I packages\KoreBuild\build -f makefile.shade %* diff --git a/build.sh b/build.sh index c7873ef58e..350d7e389a 100644 --- a/build.sh +++ b/build.sh @@ -28,11 +28,11 @@ if test ! -d packages/KoreBuild; then fi if ! type k > /dev/null 2>&1; then - source packages/KoreBuild/build/kvm.sh + source packages/KoreBuild/build/dotnetsdk.sh fi if ! type k > /dev/null 2>&1; then - kvm upgrade + dotnetsdk upgrade fi mono packages/Sake/tools/Sake.exe -I packages/KoreBuild/build -f makefile.shade "$@" diff --git a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs index 98672b3cef..c8d7f462c1 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/Properties/Resources.Designer.cs @@ -81,17 +81,17 @@ namespace Microsoft.AspNet.Razor.Runtime /// /// Parameter {0} must not contain null tag names. /// - internal static string TagNameAttribute_AdditionalTagsCannotContainNull + internal static string HtmlElementNameAttribute_AdditionalTagsCannotContainNull { - get { return GetString("TagNameAttribute_AdditionalTagsCannotContainNull"); } + get { return GetString("HtmlElementNameAttribute_AdditionalTagsCannotContainNull"); } } /// /// Parameter {0} must not contain null tag names. /// - internal static string FormatTagNameAttribute_AdditionalTagsCannotContainNull(object p0) + internal static string FormatHtmlElementNameAttribute_AdditionalTagsCannotContainNull(object p0) { - return string.Format(CultureInfo.CurrentCulture, GetString("TagNameAttribute_AdditionalTagsCannotContainNull"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("HtmlElementNameAttribute_AdditionalTagsCannotContainNull"), p0); } /// diff --git a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx index d6222dec62..6865dc5932 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/Resources.resx +++ b/src/Microsoft.AspNet.Razor.Runtime/Resources.resx @@ -131,7 +131,7 @@ Must call '{2}.{1}' before calling '{2}.{0}'. - + Parameter {0} must not contain null tag names. diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs deleted file mode 100644 index 845e608151..0000000000 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/ContentBehaviorAttribute.cs +++ /dev/null @@ -1,31 +0,0 @@ -// 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 System; -using Microsoft.AspNet.Razor.TagHelpers; - -namespace Microsoft.AspNet.Razor.Runtime.TagHelpers -{ - /// - /// Used to override 's behavior when its - /// is invoked. - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] - public sealed class ContentBehaviorAttribute : Attribute - { - /// - /// Instantiates a new instance of the class. - /// - /// The for the - /// . - public ContentBehaviorAttribute(ContentBehavior contentBehavior) - { - ContentBehavior = contentBehavior; - } - - /// - /// for the . - /// - public ContentBehavior ContentBehavior { get; private set; } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagNameAttribute.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/HtmlElementNameAttribute.cs similarity index 73% rename from src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagNameAttribute.cs rename to src/Microsoft.AspNet.Razor.Runtime/TagHelpers/HtmlElementNameAttribute.cs index 91d06d193f..f575de2c6c 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagNameAttribute.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/HtmlElementNameAttribute.cs @@ -11,29 +11,29 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// Used to override a 's default tag name target. /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] - public sealed class TagNameAttribute : Attribute + public sealed class HtmlElementNameAttribute : Attribute { /// - /// Instantiates a new instance of the class. + /// Instantiates a new instance of the class. /// /// The HTML tag name for the to target. - public TagNameAttribute([NotNull] string tag) + public HtmlElementNameAttribute([NotNull] string tag) { Tags = new[] { tag }; } /// - /// Instantiates a new instance of the class. + /// Instantiates a new instance of the class. /// /// The HTML tag name for the to target. /// Additional HTML tag names for the to target. - public TagNameAttribute([NotNull] string tag, [NotNull] params string[] additionalTags) + public HtmlElementNameAttribute([NotNull] string tag, [NotNull] params string[] additionalTags) { if (additionalTags.Contains(null)) { throw new ArgumentNullException( nameof(additionalTags), - Resources.FormatTagNameAttribute_AdditionalTagsCannotContainNull(nameof(additionalTags))); + Resources.FormatHtmlElementNameAttribute_AdditionalTagsCannotContainNull(nameof(additionalTags))); }; var allTags = new List(additionalTags); diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs index fc91494b38..2f751e3ddb 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperContext.cs @@ -1,7 +1,9 @@ // 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 System; using System.Collections.Generic; +using System.Threading.Tasks; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { @@ -10,18 +12,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// public class TagHelperContext { + private readonly Func> _getChildContentAsync; + /// /// Instantiates a new . /// /// Every attribute associated with the current HTML element. - public TagHelperContext([NotNull] IDictionary allAttributes) + /// The unique identifier for the source element this + /// applies to. + /// A delegate used to execute and retrieve the rendered child content + /// asynchronously. + public TagHelperContext([NotNull] IDictionary allAttributes, + [NotNull] string uniqueId, + [NotNull] Func> getChildContentAsync) { AllAttributes = allAttributes; + UniqueId = uniqueId; + _getChildContentAsync = getChildContentAsync; } /// /// Every attribute associated with the current HTML element. /// - public IDictionary AllAttributes { get; private set; } + public IDictionary AllAttributes { get; } + + /// + /// An identifier unique to the HTML element this context is for. + /// + public string UniqueId { get; } + + /// + /// A delegate used to execute and retrieve the rendered child content asynchronously. + /// + /// A that when executed returns content rendered by children. + public Task GetChildContentAsync() + { + return _getChildContentAsync(); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs index 51e3954325..560c723a4a 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperDescriptorFactory.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Text.RegularExpressions; using Microsoft.AspNet.Razor.TagHelpers; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers @@ -15,6 +16,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public static class TagHelperDescriptorFactory { private const string TagHelperNameEnding = "TagHelper"; + private const string HtmlCaseRegexReplacement = "-$1$2"; + + // This matches the following AFTER the start of the input string (MATCH). + // Any letter/number followed by an uppercase letter then lowercase letter: 1(Aa), a(Aa), A(Aa) + // Any lowercase letter followed by an uppercase letter: a(A) + // Each match is then prefixed by a "-" via the ToHtmlCase method. + private static readonly Regex HtmlCaseRegex = + new Regex("(? new TagHelperDescriptor(tagName, typeName, assemblyName, - contentBehavior, attributeDescriptors)); } private static IEnumerable GetTagNames(Type tagHelperType) { var typeInfo = tagHelperType.GetTypeInfo(); - var attributes = typeInfo.GetCustomAttributes(inherit: false); + var attributes = typeInfo.GetCustomAttributes(inherit: false); // If there isn't an attribute specifying the tag name derive it from the name if (!attributes.Any()) @@ -55,7 +62,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers name = name.Substring(0, name.Length - TagHelperNameEnding.Length); } - return new[] { name }; + return new[] { ToHtmlCase(name) }; } // Remove duplicate tag names. @@ -75,21 +82,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var attributeNameAttribute = property.GetCustomAttribute(inherit: false); var attributeName = attributeNameAttribute != null ? attributeNameAttribute.Name : - property.Name; + ToHtmlCase(property.Name); return new TagHelperAttributeDescriptor(attributeName, property.Name, property.PropertyType.FullName); } - private static ContentBehavior GetContentBehavior(Type type) - { - var typeInfo = type.GetTypeInfo(); - var contentBehaviorAttribute = typeInfo.GetCustomAttribute(inherit: false); - - return contentBehaviorAttribute != null ? - contentBehaviorAttribute.ContentBehavior : - ContentBehavior.None; - } - private static bool IsValidProperty(PropertyInfo property) { return property.GetMethod != null && @@ -97,5 +94,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers property.SetMethod != null && property.SetMethod.IsPublic; } + + /// + /// Converts from pascal/camel case to lower kebab-case. + /// + /// + /// SomeThing => some-thing + /// capsONInside => caps-on-inside + /// CAPSOnOUTSIDE => caps-on-outside + /// ALLCAPS => allcaps + /// One1Two2Three3 => one1-two2-three3 + /// ONE1TWO2THREE3 => one1two2three3 + /// First_Second_ThirdHi => first_second_third-hi + /// + private static string ToHtmlCase(string name) + { + return HtmlCaseRegex.Replace(name, HtmlCaseRegexReplacement).ToLowerInvariant(); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperExecutionContext.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperExecutionContext.cs index 046ff548bb..03593d80fa 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperExecutionContext.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperExecutionContext.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { @@ -12,28 +14,73 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public class TagHelperExecutionContext { private readonly List _tagHelpers; + private readonly Func _executeChildContentAsync; + private readonly Action _startWritingScope; + private readonly Func _endWritingScope; + private string _childContent; + + /// + /// Internal for testing purposes only. + /// + internal TagHelperExecutionContext(string tagName) + : this(tagName, + uniqueId: string.Empty, + executeChildContentAsync: async () => await Task.FromResult(result: true), + startWritingScope: () => { }, + endWritingScope: () => new StringWriter()) + { + } /// /// Instantiates a new . /// /// The HTML tag name in the Razor source. - public TagHelperExecutionContext([NotNull] string tagName) + /// An identifier unique to the HTML element this context is for. + /// A delegate used to execute the child content asynchronously. + /// A delegate used to start a writing scope in a Razor page. + /// A delegate used to end a writing scope in a Razor page. + public TagHelperExecutionContext([NotNull] string tagName, + [NotNull] string uniqueId, + [NotNull] Func executeChildContentAsync, + [NotNull] Action startWritingScope, + [NotNull] Func endWritingScope) { + _tagHelpers = new List(); + _executeChildContentAsync = executeChildContentAsync; + _startWritingScope = startWritingScope; + _endWritingScope = endWritingScope; + AllAttributes = new Dictionary(StringComparer.OrdinalIgnoreCase); HTMLAttributes = new Dictionary(StringComparer.OrdinalIgnoreCase); - _tagHelpers = new List(); TagName = tagName; + UniqueId = uniqueId; + } + + /// + /// Indicates if has been called. + /// + public bool ChildContentRetrieved + { + get + { + return _childContent != null; + } } /// /// HTML attributes. /// - public IDictionary HTMLAttributes { get; private set; } + public IDictionary HTMLAttributes { get; } /// /// bound attributes and HTML attributes. /// - public IDictionary AllAttributes { get; private set; } + public IDictionary AllAttributes { get; } + + /// + /// An identifier unique to the HTML element this context is for. + /// + public string UniqueId { get; } /// /// s that should be run. @@ -49,7 +96,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// /// The HTML tag name in the Razor source. /// - public string TagName { get; private set; } + public string TagName { get; } /// /// The s' output. @@ -85,5 +132,34 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { AllAttributes.Add(name, value); } + + /// + /// Executes the child content asynchronously. + /// + /// A which on completion executes all child content. + public Task ExecuteChildContentAsync() + { + return _executeChildContentAsync(); + } + + /// + /// Execute and retrieve the rendered child content asynchronously. + /// + /// A that on completion returns the rendered child content. + /// + /// Child content is only executed once. Successive calls to this method or successive executions of the + /// returned return a cached result. + /// + public async Task GetChildContentAsync() + { + if (_childContent == null) + { + _startWritingScope(); + await _executeChildContentAsync(); + _childContent = _endWritingScope().ToString(); + } + + return _childContent; + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs index 4cab64f12d..54e54e9f9c 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperOutput.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public class TagHelperOutput { private string _content; - private string _tagName; + private bool _contentSet; // Internal for testing internal TagHelperOutput(string tagName) @@ -24,48 +24,39 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Attributes = new Dictionary(StringComparer.OrdinalIgnoreCase); } - // Internal for testing - internal TagHelperOutput(string tagName, [NotNull] IDictionary attributes) - : this(tagName, attributes, string.Empty) - { - } - /// /// Instantiates a new instance of . /// /// The HTML element's tag name. /// The HTML attributes. - /// The HTML element's content. - public TagHelperOutput(string tagName, - [NotNull] IDictionary attributes, - string content) + public TagHelperOutput(string tagName, [NotNull] IDictionary attributes) { TagName = tagName; - Content = content; Attributes = new Dictionary(attributes, StringComparer.OrdinalIgnoreCase); + PreContent = string.Empty; + _content = string.Empty; + PostContent = string.Empty; } /// /// The HTML element's tag name. /// /// - /// A whitespace value results in no start or end tag being rendered. + /// A whitespace or null value results in no start or end tag being rendered. /// - public string TagName - { - get - { - return _tagName; - } - set - { - _tagName = value ?? string.Empty; - } - } + public string TagName { get; set; } /// - /// The HTML element's content. + /// The HTML element's pre content. /// + /// Value is prepended to the 's final output. + public string PreContent { get; set; } + + /// + /// The HTML element's main content. + /// + /// Value occurs in the 's final output after and + /// before public string Content { get @@ -74,7 +65,25 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers } set { - _content = value ?? string.Empty; + _contentSet = true; + _content = value; + } + } + + /// + /// The HTML element's post content. + /// + /// Value is appended to the 's final output. + public string PostContent { get; set; } + + /// + /// true if has been set, false otherwise. + /// + public bool ContentSet + { + get + { + return _contentSet; } } @@ -86,7 +95,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// /// The HTML element's attributes. /// - public IDictionary Attributes { get; private set; } + public IDictionary Attributes { get; } /// /// Generates the 's start tag. @@ -126,6 +135,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return sb.ToString(); } + /// + /// Generates the 's . + /// + /// string.Empty if is true. + /// otherwise. + /// + public string GeneratePreContent() + { + if (SelfClosing) + { + return string.Empty; + } + + return PreContent; + } + /// /// Generates the 's body. /// @@ -141,6 +166,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return Content; } + /// + /// Generates the 's . + /// + /// string.Empty if is true. + /// otherwise. + /// + public string GeneratePostContent() + { + if (SelfClosing) + { + return string.Empty; + } + + return PostContent; + } + /// /// Generates the 's end tag. /// @@ -155,5 +196,20 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return string.Format(CultureInfo.InvariantCulture, "", TagName); } + + /// + /// Changes to generate nothing. + /// + /// + /// Sets , , , and + /// to null to suppress output. + /// + public void SuppressOutput() + { + TagName = null; + PreContent = null; + Content = null; + PostContent = null; + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs index 5d8fad474e..8da31ff45f 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperRunner.cs @@ -1,7 +1,6 @@ // 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 System.IO; using System.Threading.Tasks; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers @@ -14,33 +13,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// /// Calls the method on s. /// - /// Contains information associated with running s. + /// Contains information associated with running s. + /// /// Resulting from processing all of the - /// 's s. - public async Task RunAsync([NotNull] TagHelperExecutionContext context) + /// 's s. + public async Task RunAsync([NotNull] TagHelperExecutionContext executionContext) { - return await RunAsyncCore(context, string.Empty); - } - - /// - /// Calls the method on s. - /// - /// Contains information associated with running s. - /// Contains the buffered content of the current HTML tag. - /// Resulting from processing all of the - /// 's s. - public async Task RunAsync([NotNull] TagHelperExecutionContext context, - [NotNull] TextWriter bufferedBody) - { - return await RunAsyncCore(context, bufferedBody.ToString()); - } - - private async Task RunAsyncCore(TagHelperExecutionContext executionContext, string outputContent) - { - var tagHelperContext = new TagHelperContext(executionContext.AllAttributes); - var tagHelperOutput = new TagHelperOutput(executionContext.TagName, - executionContext.HTMLAttributes, - outputContent); + var tagHelperContext = new TagHelperContext( + executionContext.AllAttributes, + executionContext.UniqueId, + executionContext.GetChildContentAsync); + var tagHelperOutput = new TagHelperOutput(executionContext.TagName, executionContext.HTMLAttributes); foreach (var tagHelper in executionContext.TagHelpers) { diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperScopeManager.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperScopeManager.cs index 7ce38b69ba..b89eb89eda 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperScopeManager.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperScopeManager.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { @@ -25,10 +27,22 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers /// Starts a scope. /// /// The HTML tag name that the scope is associated with. + /// An identifier unique to the HTML element this scope is for. + /// A delegate used to execute the child content asynchronously. + /// A delegate used to start a writing scope in a Razor page. + /// A delegate used to end a writing scope in a Razor page. /// A to use. - public TagHelperExecutionContext Begin(string tagName) + public TagHelperExecutionContext Begin([NotNull] string tagName, + [NotNull] string uniqueId, + [NotNull] Func executeChildContentAsync, + [NotNull] Action startWritingScope, + [NotNull] Func endWritingScope) { - var executionContext = new TagHelperExecutionContext(tagName); + var executionContext = new TagHelperExecutionContext(tagName, + uniqueId, + executeChildContentAsync, + startWritingScope, + endWritingScope); _executionScopes.Push(executionContext); diff --git a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs index a88752ea37..60a399140e 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs +++ b/src/Microsoft.AspNet.Razor.Runtime/TagHelpers/TagHelperTypeResolver.cs @@ -53,7 +53,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers IEnumerable libraryTypes; try { - libraryTypes = GetLibraryDefinedTypes(assemblyName); + libraryTypes = GetExportedTypes(assemblyName); } catch (Exception ex) { @@ -72,8 +72,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers return validTagHelpers.Select(type => type.AsType()); } - // Internal for testing, don't want to be loading assemblies during a test. - internal virtual IEnumerable GetLibraryDefinedTypes(AssemblyName assemblyName) + /// + /// Returns all exported types from the given + /// + /// The to get s from. + /// + /// An of types exported from the given . + /// + protected virtual IEnumerable GetExportedTypes(AssemblyName assemblyName) { var assembly = Assembly.Load(assemblyName); diff --git a/src/Microsoft.AspNet.Razor.Runtime/project.json b/src/Microsoft.AspNet.Razor.Runtime/project.json index f23e454816..3728d59c00 100644 --- a/src/Microsoft.AspNet.Razor.Runtime/project.json +++ b/src/Microsoft.AspNet.Razor.Runtime/project.json @@ -9,7 +9,8 @@ "aspnet50": { }, "aspnetcore50": { "dependencies": { - "System.Reflection.Extensions": "4.0.0-beta-*" + "System.Reflection.Extensions": "4.0.0-beta-*", + "System.Text.RegularExpressions": "4.0.10-beta-*" } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/CodeBuilderContext.cs b/src/Microsoft.AspNet.Razor/Generator/CodeBuilderContext.cs index 5d7897389c..9e84a7da46 100644 --- a/src/Microsoft.AspNet.Razor/Generator/CodeBuilderContext.cs +++ b/src/Microsoft.AspNet.Razor/Generator/CodeBuilderContext.cs @@ -1,6 +1,8 @@ // 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; + namespace Microsoft.AspNet.Razor.Generator { /// @@ -12,9 +14,14 @@ namespace Microsoft.AspNet.Razor.Generator /// Instantiates a new instance of the object. /// /// A to copy information from. - public CodeBuilderContext(CodeGeneratorContext generatorContext) + /// + /// The used to collect s encountered + /// when parsing the current Razor document. + /// + public CodeBuilderContext(CodeGeneratorContext generatorContext, ParserErrorSink errorSink) : base(generatorContext) { + ErrorSink = errorSink; ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput; } @@ -23,9 +30,11 @@ namespace Microsoft.AspNet.Razor.Generator string className, string rootNamespace, string sourceFile, - bool shouldGenerateLinePragmas) + bool shouldGenerateLinePragmas, + ParserErrorSink errorSink) : base(host, className, rootNamespace, sourceFile, shouldGenerateLinePragmas) { + ErrorSink = errorSink; ExpressionRenderingMode = ExpressionRenderingMode.WriteToOutput; } @@ -56,5 +65,10 @@ namespace Microsoft.AspNet.Razor.Generator /// . /// public string Checksum { get; set; } + + /// + /// Used to aggregate s. + /// + public ParserErrorSink ErrorSink { get; } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeBuilder.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeBuilder.cs index dc2aa52d1a..7d6dd4e613 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeBuilder.cs @@ -43,8 +43,6 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp // Separate the usings and the class writer.WriteLine(); - new CSharpClassAttributeVisitor(writer, Context).Accept(Tree.Chunks); - using (BuildClassDeclaration(writer)) { if (Host.DesignTimeMode) 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 a47a0a436a..25be0ecb81 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpTagHelperCodeRenderer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.TagHelpers; namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp @@ -57,14 +58,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp { var tagHelperDescriptors = chunk.Descriptors; - // Find the first content behavior that doesn't have a content behavior of None. - // The resolver restricts content behavior collisions so the first one that's not None will be - // the content behavior we need to abide by. None can work in unison with other ContentBehaviors. - var contentBehavior = tagHelperDescriptors.Select(descriptor => descriptor.ContentBehavior) - .FirstOrDefault( - behavior => behavior != ContentBehavior.None); - - RenderBeginTagHelperScope(chunk.TagName); + RenderBeginTagHelperScope(chunk.TagName, chunk.Children); RenderTagHelpersCreation(chunk); @@ -77,42 +71,15 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp RenderUnboundHTMLAttributes(unboundHTMLAttributes); - switch (contentBehavior) - { - case ContentBehavior.None: - RenderRunTagHelpers(bufferedBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); - RenderTagHelperBody(chunk.Children, bufferBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); - break; - case ContentBehavior.Append: - RenderRunTagHelpers(bufferedBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); - RenderTagHelperBody(chunk.Children, bufferBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); - RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); - break; - case ContentBehavior.Prepend: - RenderRunTagHelpers(bufferedBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); - RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); - RenderTagHelperBody(chunk.Children, bufferBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); - break; - case ContentBehavior.Replace: - RenderRunTagHelpers(bufferedBody: false); - RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); - RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); - RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); - break; - case ContentBehavior.Modify: - RenderTagHelperBody(chunk.Children, bufferBody: true); - RenderRunTagHelpers(bufferedBody: true); - RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); - RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); - RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); - break; - } + RenderRunTagHelpers(); + + RenderTagOutput(_tagHelperContext.OutputGenerateStartTagMethodName); + RenderTagOutput(_tagHelperContext.OutputGeneratePreContentMethodName); + + RenderTagHelperContent(); + + RenderTagOutput(_tagHelperContext.OutputGeneratePostContentMethodName); + RenderTagOutput(_tagHelperContext.OutputGenerateEndTagMethodName); RenderEndTagHelpersScope(); } @@ -122,11 +89,13 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp return "__" + descriptor.TypeName.Replace('.', '_'); } - private void RenderBeginTagHelperScope(string tagName) + private void RenderBeginTagHelperScope(string tagName, IList children) { // Scopes/execution contexts are a runtime feature. if (_designTimeMode) { + // Render all of the tag helper children inline for IntelliSense. + _bodyVisitor.Accept(children); return; } @@ -135,10 +104,50 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp _writer.WriteStartAssignment(ExecutionContextVariableName) .WriteStartInstanceMethodInvocation(ScopeManagerVariableName, _tagHelperContext.ScopeManagerBeginMethodName); + + // Assign a unique ID for this instance of the source HTML tag. This must be unique + // per call site, e.g. if the tag is on the view twice, there should be two IDs. _writer.WriteStringLiteral(tagName) + .WriteParameterSeparator() + .WriteStringLiteral(GenerateUniqueId()) + .WriteParameterSeparator(); + + // We remove the target writer so TagHelper authors can retrieve content. + var oldWriter = _context.TargetWriterName; + _context.TargetWriterName = null; + + // Disabling instrumentation inside TagHelper bodies since we never know if it's accurate + var oldInstrumentation = _context.Host.EnableInstrumentation; + _context.Host.EnableInstrumentation = false; + + using (_writer.BuildAsyncLambda(endLine: false)) + { + // Render all of the tag helper children. + _bodyVisitor.Accept(children); + } + + _context.Host.EnableInstrumentation = oldInstrumentation; + + _context.TargetWriterName = oldWriter; + + _writer.WriteParameterSeparator() + .Write(_tagHelperContext.StartWritingScopeMethodName) + .WriteParameterSeparator() + .Write(_tagHelperContext.EndWritingScopeMethodName) .WriteEndMethodInvocation(); } + /// + /// Generates a unique ID for an HTML element. + /// + /// + /// A globally unique ID. + /// + protected virtual string GenerateUniqueId() + { + return Guid.NewGuid().ToString("N"); + } + private void RenderTagHelpersCreation(TagHelperChunk chunk) { var tagHelperDescriptors = chunk.Descriptors; @@ -213,48 +222,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, attributeDescriptor); } - // End the assignment to the attribute. - _writer.WriteLine(";"); - // Execution contexts are a runtime feature. if (_designTimeMode) { @@ -262,20 +290,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(";"); } } } @@ -323,18 +354,74 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp } } - private void RenderTagHelperBody(IList children, bool bufferBody) + private void RenderTagHelperContent() { - // If we want to buffer the body we need to create a writing scope to capture the body content. - if (bufferBody) + // Rendering output is a runtime feature. + if (_designTimeMode) { - // Render all of the tag helper children in a buffered writing scope. - BuildBufferedWritingScope(children); + return; } - else + + _writer.Write("if (") + .Write(ExecutionContextVariableName) + .Write(".") + .Write(_tagHelperContext.ExecutionContextOutputPropertyName) + .Write(".") + .Write(_tagHelperContext.OutputContentSetPropertyName) + .WriteLine(")"); + + // At this point in the codegen, TagHelperOutput.Content is set. We need to use this to render the Content + // instead of executing the child content + using (_writer.BuildScope()) { - // Render all of the tag helper children. - _bodyVisitor.Accept(children); + RenderTagOutput(_tagHelperContext.OutputGenerateContentMethodName); + } + + _writer.Write("else if (") + .Write(ExecutionContextVariableName) + .Write(".") + .Write(_tagHelperContext.ExecutionContextChildContentRetrievedPropertyName) + .WriteLine(")"); + + // Render the body of the else if statement, at this point in the codegen the GetChildContentAsync method + // was invoked but the TagHelperOutput's Content was not set. Call into GetChildContentAsync to retrieve + // the cached value of the content so we don't execute the child content twice. + using (_writer.BuildScope()) + { + CSharpCodeVisitor.RenderPreWriteStart(_writer, _context); + + _writer.WriteInstanceMethodInvocation(ExecutionContextVariableName, + _tagHelperContext.ExecutionContextGetChildContentAsyncMethodName, + endLine: false); + + _writer.Write(".Result") + .WriteEndMethodInvocation(); + } + + _writer.WriteLine("else"); + + // Render the body of the else statement, at this point in the codegen the GetChildContentAsync method + // was not invoked and the TagHelperOutput's Content was not set. Call into ExecuteChildContentAsync to + // to execute and render child content. + using (_writer.BuildScope()) + { + if (!string.IsNullOrEmpty(_context.TargetWriterName)) + { + _writer.WriteMethodInvocation( + _tagHelperContext.StartWritingScopeMethodName, + _context.TargetWriterName); + } + + _writer.WriteInstanceMethodInvocation( + ExecutionContextVariableName, + _tagHelperContext.ExecutionContextExecuteChildContentAsyncMethodName, + endLine: false); + _writer.WriteLine(".Wait();"); + + if (!string.IsNullOrEmpty(_context.TargetWriterName)) + { + _writer.WriteMethodInvocation(_tagHelperContext.EndWritingScopeMethodName); + } } } @@ -369,7 +456,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp .WriteEndMethodInvocation(); } - private void RenderRunTagHelpers(bool bufferedBody) + private void RenderRunTagHelpers() { // No need to run anything in design time mode. if (_designTimeMode) @@ -383,36 +470,38 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp .Write(" = ") .WriteStartInstanceMethodInvocation(RunnerVariableName, _tagHelperContext.RunnerRunAsyncMethodName); - _writer.Write(ExecutionContextVariableName); - if (bufferedBody) - { - _writer.WriteParameterSeparator() - .Write(StringValueBufferVariableName); - } - - _writer.WriteEndMethodInvocation(endLine: false) + _writer.Write(ExecutionContextVariableName) + .WriteEndMethodInvocation(endLine: false) .WriteLine(".Result;"); } 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, TagHelperAttributeDescriptor attributeDescriptor) + private void RenderRawAttributeValue( + Chunk attributeValueChunk, + TagHelperAttributeDescriptor attributeDescriptor, + bool isPlainTextValue) { RenderAttributeValue( attributeDescriptor, valueRenderer: (writer) => { - writer.Write(value); - }); + var visitor = + new CSharpTagHelperAttributeValueVisitor(writer, _context, attributeDescriptor.TypeName); + visitor.Accept(attributeValueChunk); + }, + complexValue: !isPlainTextValue); } private void RenderQuotedAttributeValue(string value, TagHelperAttributeDescriptor attributeDescriptor) @@ -422,7 +511,8 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp valueRenderer: (writer) => { writer.WriteStringLiteral(value); - }); + }, + complexValue: false); } private void BuildBufferedWritingScope(Chunk htmlAttributeChunk) @@ -473,9 +563,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/CSharpClassAttributeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpClassAttributeVisitor.cs deleted file mode 100644 index 9d375d68eb..0000000000 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpClassAttributeVisitor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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; - -namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp -{ - public class CSharpClassAttributeVisitor : CodeVisitor - { - public CSharpClassAttributeVisitor(CSharpCodeWriter writer, CodeBuilderContext context) - : base(writer, context) - { } - - protected override void Visit(SessionStateChunk chunk) - { - Writer.Write("[") - .Write(typeof(RazorDirectiveAttribute).FullName) - .Write("(") - .WriteStringLiteral(SyntaxConstants.CSharp.SessionStateKeyword) - .WriteParameterSeparator() - .WriteStringLiteral(chunk.Value) - .WriteLine(")]"); - } - } -} 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/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperFieldDeclarationVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperFieldDeclarationVisitor.cs index 01e5095223..ba9532f264 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperFieldDeclarationVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpTagHelperFieldDeclarationVisitor.cs @@ -34,9 +34,13 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp // Runtime fields aren't useful during design time. if (!Context.Host.DesignTimeMode) { + // Need to disable the warning "X is assigned to but never used." for the value buffer since + // whether it's used depends on how a TagHelper is used. + Writer.WritePragma("warning disable 0414"); WritePrivateField(typeof(TextWriter).FullName, CSharpTagHelperCodeRenderer.StringValueBufferVariableName, value: null); + Writer.WritePragma("warning restore 0414"); WritePrivateField(_tagHelperContext.ExecutionContextTypeName, CSharpTagHelperCodeRenderer.ExecutionContextVariableName, diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs index 7352a21a1d..9a901d41e0 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs @@ -113,10 +113,6 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler { Visit((ChunkBlock)chunk); } - else if (chunk is SessionStateChunk) - { - Visit((SessionStateChunk)chunk); - } } protected abstract void Visit(LiteralChunk chunk); @@ -138,6 +134,5 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler protected abstract void Visit(TemplateChunk chunk); protected abstract void Visit(SetLayoutChunk chunk); protected abstract void Visit(ExpressionBlockChunk chunk); - protected abstract void Visit(SessionStateChunk chunk); } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs index 1b2a608884..955e4069c6 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs @@ -66,8 +66,5 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler protected override void Visit(SetLayoutChunk chunk) { } - protected override void Visit(SessionStateChunk chunk) - { - } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/SessionStateChunk.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/SessionStateChunk.cs deleted file mode 100644 index 42c84b5c43..0000000000 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/SessionStateChunk.cs +++ /dev/null @@ -1,10 +0,0 @@ -// 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. - -namespace Microsoft.AspNet.Razor.Generator.Compiler -{ - public class SessionStateChunk : Chunk - { - public string Value { get; set; } - } -} diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs index 5ac2422251..35a854a00a 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs @@ -145,14 +145,6 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler }, association, topLevel: true); } - public void AddSessionStateChunk(string value, SyntaxTreeNode association) - { - AddChunk(new SessionStateChunk - { - Value = value - }, association, topLevel: true); - } - public T StartChunkBlock(SyntaxTreeNode association) where T : ChunkBlock, new() { return StartChunkBlock(association, topLevel: false); diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs index 6bf06bd1ec..60778a34e6 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/LineMapping.cs @@ -1,6 +1,8 @@ // 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 System.Globalization; + namespace Microsoft.AspNet.Razor.Generator.Compiler { public class LineMapping @@ -40,5 +42,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler { return !left.Equals(right); } + + public override string ToString() + { + return string.Format(CultureInfo.CurrentUICulture, "{0} -> {1}", DocumentLocation, GeneratedLocation); + } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs index 53a372892e..9d3d6e2361 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/LineMappings/MappingLocation.cs @@ -1,6 +1,7 @@ // 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 System.Globalization; using Microsoft.AspNet.Razor.Text; namespace Microsoft.AspNet.Razor.Generator.Compiler @@ -37,6 +38,16 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler return base.GetHashCode(); } + public override string ToString() + { + return string.Format( + CultureInfo.CurrentCulture, "({0}:{1},{2} [{3}])", + AbsoluteIndex, + LineIndex, + CharacterIndex, + ContentLength); + } + public static bool operator ==(MappingLocation left, MappingLocation right) { return left.Equals(right); diff --git a/src/Microsoft.AspNet.Razor/Generator/GeneratedTagHelperContext.cs b/src/Microsoft.AspNet.Razor/Generator/GeneratedTagHelperContext.cs index be427b916b..bafd71751f 100644 --- a/src/Microsoft.AspNet.Razor/Generator/GeneratedTagHelperContext.cs +++ b/src/Microsoft.AspNet.Razor/Generator/GeneratedTagHelperContext.cs @@ -17,9 +17,15 @@ namespace Microsoft.AspNet.Razor.Generator RunnerRunAsyncMethodName = "RunAsync"; ScopeManagerBeginMethodName = "Begin"; ScopeManagerEndMethodName = "End"; + OutputContentSetPropertyName = "ContentSet"; OutputGenerateStartTagMethodName = "GenerateStartTag"; + OutputGeneratePreContentMethodName = "GeneratePreContent"; OutputGenerateContentMethodName = "GenerateContent"; + OutputGeneratePostContentMethodName = "GeneratePostContent"; OutputGenerateEndTagMethodName = "GenerateEndTag"; + ExecutionContextChildContentRetrievedPropertyName = "ChildContentRetrieved"; + ExecutionContextExecuteChildContentAsyncMethodName = "ExecuteChildContentAsync"; + ExecutionContextGetChildContentAsyncMethodName = "GetChildContentAsync"; ExecutionContextAddMethodName = "Add"; ExecutionContextAddTagHelperAttributeMethodName = "AddTagHelperAttribute"; ExecutionContextAddHtmlAttributeMethodName = "AddHtmlAttribute"; @@ -51,21 +57,52 @@ namespace Microsoft.AspNet.Razor.Generator /// public string ScopeManagerEndMethodName { get; set; } + /// + /// The name of the property used to determine if a tag helper output's content was set. + /// + public string OutputContentSetPropertyName { get; set; } + /// /// The name of the method used to generate a tag helper output's start tag. /// public string OutputGenerateStartTagMethodName { get; set; } + /// + /// The name of the method used to generate a tag helper output's pre content. + /// + public string OutputGeneratePreContentMethodName { get; set; } + /// /// The name of the method used to generate a tag helper output's content. /// public string OutputGenerateContentMethodName { get; set; } + /// + /// The name of the method used to generate a tag helper output's post-content. + /// + public string OutputGeneratePostContentMethodName { get; set; } + /// /// The name of the method used to generate a tag helper output's end tag. /// public string OutputGenerateEndTagMethodName { get; set; } + /// + /// The name of the method used to get a + /// that executes tag helper child content. + /// + public string ExecutionContextChildContentRetrievedPropertyName { get; set; } + + /// + /// + public string ExecutionContextExecuteChildContentAsyncMethodName { get; set; } + + /// + /// The name of the method used to execute and retrieve tag helper + /// child content. + /// + public string ExecutionContextGetChildContentAsyncMethodName { get; set; } + /// /// The name of the method used to add tag helper attributes. /// diff --git a/src/Microsoft.AspNet.Razor/Generator/RazorDirectiveAttributeCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/RazorDirectiveAttributeCodeGenerator.cs deleted file mode 100644 index 177a91e303..0000000000 --- a/src/Microsoft.AspNet.Razor/Generator/RazorDirectiveAttributeCodeGenerator.cs +++ /dev/null @@ -1,53 +0,0 @@ -// 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 System; -using Microsoft.AspNet.Razor.Parser; -using Microsoft.AspNet.Razor.Parser.SyntaxTree; - -namespace Microsoft.AspNet.Razor.Generator -{ - public class RazorDirectiveAttributeCodeGenerator : SpanCodeGenerator - { - public RazorDirectiveAttributeCodeGenerator(string name, string value) - { - if (String.IsNullOrEmpty(name)) - { - throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "name"); - } - Name = name; - Value = value ?? String.Empty; // Coerce to empty string if it was null. - } - - public string Name { get; private set; } - - public string Value { get; private set; } - - public override void GenerateCode(Span target, CodeGeneratorContext context) - { - if (Name == SyntaxConstants.CSharp.SessionStateKeyword) - { - context.CodeTreeBuilder.AddSessionStateChunk(Value, target); - } - } - - public override string ToString() - { - return "Directive: " + Name + ", Value: " + Value; - } - - public override bool Equals(object obj) - { - var other = obj as RazorDirectiveAttributeCodeGenerator; - return other != null && - Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) && - Value.Equals(other.Value, StringComparison.OrdinalIgnoreCase); - } - - public override int GetHashCode() - { - return Tuple.Create(Name.ToUpperInvariant(), Value.ToUpperInvariant()) - .GetHashCode(); - } - } -} diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs index 570af0fa60..d09b718051 100644 --- a/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs +++ b/src/Microsoft.AspNet.Razor/Generator/TagHelperCodeGenerator.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.AspNet.Razor.Generator.Compiler; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Parser.TagHelpers; using Microsoft.AspNet.Razor.TagHelpers; +using Microsoft.AspNet.Razor.Text; namespace Microsoft.AspNet.Razor.Generator { @@ -60,10 +62,13 @@ namespace Microsoft.AspNet.Razor.Generator attribute.Value.Accept(codeGenerator); var chunks = codeGenerator.Context.CodeTreeBuilder.CodeTree.Chunks; + var first = chunks.FirstOrDefault(); attributes[attribute.Key] = new ChunkBlock { - Children = chunks + Association = first?.Association, + Children = chunks, + Start = first == null ? SourceLocation.Zero : first.Start }; // Reset the code tree builder so we can build a new one for the next attribute diff --git a/src/Microsoft.AspNet.Razor/GeneratorResults.cs b/src/Microsoft.AspNet.Razor/GeneratorResults.cs index ec02a3f935..2698cc6abe 100644 --- a/src/Microsoft.AspNet.Razor/GeneratorResults.cs +++ b/src/Microsoft.AspNet.Razor/GeneratorResults.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.TagHelpers; @@ -24,7 +25,7 @@ namespace Microsoft.AspNet.Razor [NotNull] CodeTree codeTree) : this(parserResults.Document, parserResults.TagHelperDescriptors, - parserResults.ParserErrors, + parserResults.ErrorSink, codeBuilderResult, codeTree) { @@ -34,35 +35,21 @@ namespace Microsoft.AspNet.Razor /// Instantiates a new instance. /// /// The for the syntax tree. - /// s for the document. - /// s encountered when parsing the document. + /// + /// The s that apply to the current Razor document. + /// + /// + /// The used to collect s encountered when parsing the + /// current Razor document. + /// /// The results of generating code for the document. /// A for the document. public GeneratorResults([NotNull] Block document, [NotNull] IEnumerable tagHelperDescriptors, - [NotNull] IList parserErrors, + [NotNull] ParserErrorSink errorSink, [NotNull] CodeBuilderResult codeBuilderResult, [NotNull] CodeTree codeTree) - : this(parserErrors.Count == 0, document, tagHelperDescriptors, parserErrors, codeBuilderResult, codeTree) - { - } - - /// - /// Instantiates a new instance. - /// - /// true if parsing was successful, false otherwise. - /// The for the syntax tree. - /// s for the document. - /// s encountered when parsing the document. - /// The results of generating code for the document. - /// A for the document. - protected GeneratorResults(bool success, - [NotNull] Block document, - [NotNull] IEnumerable tagHelperDescriptors, - [NotNull] IList parserErrors, - [NotNull] CodeBuilderResult codeBuilderResult, - [NotNull] CodeTree codeTree) - : base(success, document, tagHelperDescriptors, parserErrors) + : base(document, tagHelperDescriptors, errorSink) { GeneratedCode = codeBuilderResult.Code; DesignTimeLineMappings = codeBuilderResult.DesignTimeLineMappings; diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs index 501d9d662a..8ba70aee02 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs @@ -25,7 +25,6 @@ namespace Microsoft.AspNet.Razor.Parser MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword); MapDirectives(HelperDirective, SyntaxConstants.CSharp.HelperKeyword); MapDirectives(LayoutDirective, SyntaxConstants.CSharp.LayoutKeyword); - MapDirectives(SessionStateDirective, SyntaxConstants.CSharp.SessionStateKeyword); } protected virtual void AddTagHelperDirective() @@ -58,65 +57,6 @@ namespace Microsoft.AspNet.Razor.Parser Output(SpanKind.MetaCode, foundNewline ? AcceptedCharacters.None : AcceptedCharacters.Any); } - protected virtual void SessionStateDirective() - { - AssertDirective(SyntaxConstants.CSharp.SessionStateKeyword); - AcceptAndMoveNext(); - - SessionStateDirectiveCore(); - } - - protected void SessionStateDirectiveCore() - { - SessionStateTypeDirective(RazorResources.ParserEror_SessionDirectiveMissingValue, (key, value) => new RazorDirectiveAttributeCodeGenerator(key, value)); - } - - protected void SessionStateTypeDirective(string noValueError, Func createCodeGenerator) - { - // Set the block type - Context.CurrentBlock.Type = BlockType.Directive; - - // Accept whitespace - var remainingWs = AcceptSingleWhiteSpaceCharacter(); - - if (Span.Symbols.Count > 1) - { - Span.EditHandler.AcceptedCharacters = AcceptedCharacters.None; - } - - Output(SpanKind.MetaCode); - - if (remainingWs != null) - { - Accept(remainingWs); - } - AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); - - // Parse a Type Name - if (!ValidSessionStateValue()) - { - Context.OnError(CurrentLocation, noValueError); - } - - // Pull out the type name - var sessionStateValue = String.Concat( - Span.Symbols - .Cast() - .Select(sym => sym.Content)).Trim(); - - // Set up code generation - Span.CodeGenerator = createCodeGenerator(SyntaxConstants.CSharp.SessionStateKeyword, sessionStateValue); - - // Output the span and finish the block - CompleteBlock(); - Output(SpanKind.Code); - } - - protected virtual bool ValidSessionStateValue() - { - return Optional(CSharpSymbolType.Identifier); - } - [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "Coupling will be reviewed at a later date")] [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "C# Keywords are always lower-case")] protected virtual void HelperDirective() diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs index b1394b47b7..6906293549 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs @@ -37,7 +37,6 @@ namespace Microsoft.AspNet.Razor.Parser "namespace", "class", "layout", - "sessionstate" }; private Dictionary _directiveParsers = new Dictionary(); diff --git a/src/Microsoft.AspNet.Razor/Parser/ParserContext.cs b/src/Microsoft.AspNet.Razor/Parser/ParserContext.cs index fa0a9bbf4b..95d8a30325 100644 --- a/src/Microsoft.AspNet.Razor/Parser/ParserContext.cs +++ b/src/Microsoft.AspNet.Razor/Parser/ParserContext.cs @@ -230,7 +230,7 @@ namespace Microsoft.AspNet.Razor.Parser // TagHelperDescriptors are not found by default. The RazorParser is responsible // for identifying TagHelperDescriptors and rebuilding ParserResults. tagHelperDescriptors: Enumerable.Empty(), - parserErrors: _errorSink.Errors.ToList()); + errorSink: _errorSink); } [Conditional("DEBUG")] diff --git a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs index 01a04ed8e3..2d538e347c 100644 --- a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs @@ -197,7 +197,7 @@ namespace Microsoft.AspNet.Razor.Parser } // Return the new result - return new ParserResults(syntaxTree, descriptors, errorSink.Errors.ToList()); + return new ParserResults(syntaxTree, descriptors, errorSink); } /// diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs index 2160cd1090..f0cfc808ec 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs @@ -28,7 +28,6 @@ namespace Microsoft.AspNet.Razor.Parser public static readonly string NamespaceKeyword = "namespace"; public static readonly string ClassKeyword = "class"; public static readonly string LayoutKeyword = "layout"; - public static readonly string SessionStateKeyword = "sessionstate"; } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs index f4fe7dac9a..798b99771f 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlock.cs @@ -6,6 +6,7 @@ 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; using Microsoft.Internal.Web.Utils; @@ -27,6 +28,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers : base(source.Type, source.Children, source.CodeGenerator) { TagName = source.TagName; + Descriptors = source.Descriptors; Attributes = new Dictionary(source.Attributes); _start = source.Start; @@ -38,6 +40,11 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers } } + /// + /// s for the HTML element. + /// + public IEnumerable Descriptors { get; } + /// /// The HTML attributes. /// diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs index 1e604c7ae2..629e955d90 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockBuilder.cs @@ -1,14 +1,11 @@ // 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 System; using System.Collections.Generic; -using System.Linq; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.TagHelpers; using Microsoft.AspNet.Razor.Text; -using Microsoft.AspNet.Razor.Tokenizer.Symbols; namespace Microsoft.AspNet.Razor.Parser.TagHelpers { @@ -18,7 +15,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers public class TagHelperBlockBuilder : BlockBuilder { /// - /// Instantiates a new instance based on given the + /// Instantiates a new instance based on the given /// . /// /// The original to copy data from. @@ -26,6 +23,7 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers : base(original) { TagName = original.TagName; + Descriptors = original.Descriptors; Attributes = new Dictionary(original.Attributes); } @@ -35,19 +33,21 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers /// and from the . /// /// An HTML tag name. + /// Starting location of the . + /// Attributes of the . /// The s associated with the current HTML /// tag. - /// The that contains all information about the start - /// of the HTML element. - public TagHelperBlockBuilder(string tagName, IEnumerable descriptors, Block startTag) + public TagHelperBlockBuilder(string tagName, + SourceLocation start, + IDictionary attributes, + IEnumerable descriptors) { TagName = tagName; + Start = start; + Descriptors = descriptors; + Type = BlockType.Tag; CodeGenerator = new TagHelperCodeGenerator(descriptors); - Type = startTag.Type; - Attributes = GetTagAttributes(startTag, descriptors); - - // There will always be at least one child for the '<'. - Start = startTag.Children.First().Start; + Attributes = new Dictionary(attributes); } // Internal for testing @@ -67,6 +67,11 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers } } + /// + /// s for the HTML element. + /// + public IEnumerable Descriptors { get; } + /// /// The HTML attributes. /// @@ -106,280 +111,5 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers /// The starting of the tag helper. /// public SourceLocation Start { get; private set; } - - private static IDictionary GetTagAttributes( - Block tagBlock, - IEnumerable descriptors) - { - var attributes = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // Build a dictionary so we can easily lookup expected attribute value lookups - IReadOnlyDictionary attributeValueTypes = - descriptors.SelectMany(descriptor => descriptor.Attributes) - .Distinct(TagHelperAttributeDescriptorComparer.Default) - .ToDictionary(descriptor => descriptor.Name, - descriptor => descriptor.TypeName, - StringComparer.OrdinalIgnoreCase); - - // We skip the first child "" or "/>". - // The -2 accounts for both the start and end tags. - var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - 2); - - foreach (var child in attributeChildren) - { - KeyValuePair attribute; - - if (child.IsBlock) - { - attribute = ParseBlock((Block)child, attributeValueTypes); - } - else - { - attribute = ParseSpan((Span)child, attributeValueTypes); - } - - attributes.Add(attribute.Key, attribute.Value); - } - - return attributes; - } - - // This method handles cases when the attribute is a simple span attribute such as - // class="something moresomething". This does not handle complex attributes such as - // class="@myclass". Therefore the span.Content is equivalent to the entire attribute. - private static KeyValuePair ParseSpan( - Span span, - IReadOnlyDictionary attributeValueTypes) - { - var afterEquals = false; - var builder = new SpanBuilder - { - CodeGenerator = span.CodeGenerator, - EditHandler = span.EditHandler, - Kind = span.Kind - }; - var htmlSymbols = span.Symbols.OfType().ToArray(); - var symbolOffset = 1; - string name = null; - - // Iterate down through the symbols to find the name and the start of the value. - // We subtract the symbolOffset so we don't accept an ending quote of a span. - for (var i = 0; i < htmlSymbols.Length - symbolOffset; i++) - { - var symbol = htmlSymbols[i]; - - if (name == null && symbol.Type == HtmlSymbolType.Text) - { - name = symbol.Content; - } - else if (afterEquals) - { - builder.Accept(symbol); - } - else if (symbol.Type == HtmlSymbolType.Equals) - { - // We've found an '=' symbol, this means that the coming symbols will either be a quote - // or value (in the case that the value is unquoted). - // Spaces after/before the equal symbol are not yet supported: - // https://github.com/aspnet/Razor/issues/123 - - // TODO: Handle malformed tags, if there's an '=' then there MUST be a value. - // https://github.com/aspnet/Razor/issues/104 - - // Check for attribute start values, aka single or double quote - if (IsQuote(htmlSymbols[i + 1])) - { - // Move past the attribute start so we can accept the true value. - i++; - } - else - { - // Set the symbol offset to 0 so we don't attempt to skip an end quote that doesn't exist. - symbolOffset = 0; - } - - afterEquals = true; - } - } - - return CreateMarkupAttribute(name, builder, attributeValueTypes); - } - - private static KeyValuePair ParseBlock( - Block block, - IReadOnlyDictionary attributeValueTypes) - { - // TODO: Accept more than just spans: https://github.com/aspnet/Razor/issues/96. - // The first child will only ever NOT be a Span if a user is doing something like: - // - - var childSpan = block.Children.First() as Span; - - if (childSpan == null) - { - throw new InvalidOperationException(RazorResources.TagHelpers_CannotHaveCSharpInTagDeclaration); - } - - var builder = new BlockBuilder(block); - - // If there's only 1 child it means that it's plain text inside of the attribute. - // i.e.
- if (builder.Children.Count == 1) - { - return ParseSpan(childSpan, attributeValueTypes); - } - - var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.Text); - var name = textSymbol != null ? textSymbol.Content : null; - - if (name == null) - { - throw new InvalidOperationException(RazorResources.TagHelpers_AttributesMustHaveAName); - } - - // Remove first child i.e. foo=" - builder.Children.RemoveAt(0); - - // Grabbing last child to check if the attribute value is quoted. - var endNode = block.Children.Last(); - if (!endNode.IsBlock) - { - var endSpan = (Span)endNode; - var endSymbol = (HtmlSymbol)endSpan.Symbols.Last(); - - // Checking to see if it's a quoted attribute, if so we should remove end quote - if (IsQuote(endSymbol)) - { - builder.Children.RemoveAt(builder.Children.Count - 1); - } - } - - // We need to rebuild the code generators of the builder and its children (this is needed to - // ensure we don't do special attribute code generation since this is a tag helper). - block = RebuildCodeGenerators(builder.Build()); - - // If there's only 1 child at this point its value could be a simple markup span (treated differently than - // block level elements for attributes). - if (block.Children.Count() == 1) - { - var child = block.Children.First() as Span; - - if (child != null) - { - // After pulling apart the block we just have a value span. - - var spanBuilder = new SpanBuilder(child); - - return CreateMarkupAttribute(name, spanBuilder, attributeValueTypes); - } - } - - return new KeyValuePair(name, block); - } - - private static Block RebuildCodeGenerators(Block block) - { - var builder = new BlockBuilder(block); - - var isDynamic = builder.CodeGenerator is DynamicAttributeBlockCodeGenerator; - - // We don't want any attribute specific logic here, null out the block code generator. - if (isDynamic || builder.CodeGenerator is AttributeBlockCodeGenerator) - { - builder.CodeGenerator = BlockCodeGenerator.Null; - } - - for (var i = 0; i < builder.Children.Count; i++) - { - var child = builder.Children[i]; - - if (child.IsBlock) - { - // The child is a block, recurse down into the block to rebuild its children - builder.Children[i] = RebuildCodeGenerators((Block)child); - } - else - { - var childSpan = (Span)child; - ISpanCodeGenerator newCodeGenerator = null; - var literalGenerator = childSpan.CodeGenerator as LiteralAttributeCodeGenerator; - - if (literalGenerator != null) - { - if (literalGenerator.ValueGenerator == null || literalGenerator.ValueGenerator.Value == null) - { - newCodeGenerator = new MarkupCodeGenerator(); - } - else - { - newCodeGenerator = literalGenerator.ValueGenerator.Value; - } - } - else if (isDynamic && childSpan.CodeGenerator == SpanCodeGenerator.Null) - { - // Usually the dynamic code generator handles rendering the null code generators underneath - // it. This doesn't make sense in terms of tag helpers though, we need to change null code - // generators to markup code generators. - - newCodeGenerator = new MarkupCodeGenerator(); - } - - // If we have a new code generator we'll need to re-build the child - if (newCodeGenerator != null) - { - var childSpanBuilder = new SpanBuilder(childSpan) - { - CodeGenerator = newCodeGenerator - }; - - builder.Children[i] = childSpanBuilder.Build(); - } - } - } - - return builder.Build(); - } - - private static KeyValuePair CreateMarkupAttribute( - string name, - SpanBuilder builder, - IReadOnlyDictionary attributeValueTypes) - { - string attributeTypeName; - - // If the attribute was requested by the tag helper and doesn't happen to be a string then we need to treat - // its value as code. Any non-string value can be any C# value so we need to ensure the SyntaxTreeNode - // reflects that. - if (attributeValueTypes.TryGetValue(name, out attributeTypeName) && - !string.Equals(attributeTypeName, typeof(string).FullName, StringComparison.OrdinalIgnoreCase)) - { - builder.Kind = SpanKind.Code; - } - - return new KeyValuePair(name, builder.Build()); - } - - private static bool IsQuote(HtmlSymbol htmlSymbol) - { - return htmlSymbol.Type == HtmlSymbolType.DoubleQuote || - htmlSymbol.Type == HtmlSymbolType.SingleQuote; - } - - // This class is used to compare tag helper attributes by comparing only the HTML attribute name. - private class TagHelperAttributeDescriptorComparer : IEqualityComparer - { - public static readonly TagHelperAttributeDescriptorComparer Default = - new TagHelperAttributeDescriptorComparer(); - - public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY) - { - return string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.OrdinalIgnoreCase); - } - - public int GetHashCode(TagHelperAttributeDescriptor descriptor) - { - return StringComparer.OrdinalIgnoreCase.GetHashCode(descriptor.Name); - } - } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs new file mode 100644 index 0000000000..4fa31a7889 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperBlockRewriter.cs @@ -0,0 +1,375 @@ +// 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 System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.TagHelpers; +using Microsoft.AspNet.Razor.Text; +using Microsoft.AspNet.Razor.Tokenizer.Symbols; + +namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal +{ + public static class TagHelperBlockRewriter + { + public static TagHelperBlockBuilder Rewrite(string tagName, + bool validStructure, + Block tag, + IEnumerable descriptors, + ParserErrorSink errorSink) + { + // There will always be at least one child for the '<'. + var start = tag.Children.First().Start; + var attributes = GetTagAttributes(tagName, validStructure, tag, descriptors, errorSink); + + return new TagHelperBlockBuilder(tagName, start, attributes, descriptors); + } + + private static IDictionary GetTagAttributes( + string tagName, + bool validStructure, + Block tagBlock, + IEnumerable descriptors, + ParserErrorSink errorSink) + { + var attributes = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Build a dictionary so we can easily lookup expected attribute value lookups + IReadOnlyDictionary attributeValueTypes = + descriptors.SelectMany(descriptor => descriptor.Attributes) + .Distinct(TagHelperAttributeDescriptorComparer.Default) + .ToDictionary(descriptor => descriptor.Name, + descriptor => descriptor.TypeName, + StringComparer.OrdinalIgnoreCase); + + // We skip the first child "" or "/>". + // The -2 accounts for both the start and end tags. If the tag does not have a valid structure then there's + // no end tag to ignore. + var symbolOffset = validStructure ? 2 : 1; + var attributeChildren = tagBlock.Children.Skip(1).Take(tagBlock.Children.Count() - symbolOffset); + + foreach (var child in attributeChildren) + { + KeyValuePair attribute; + bool succeeded = true; + + if (child.IsBlock) + { + succeeded = TryParseBlock(tagName, (Block)child, attributeValueTypes, errorSink, out attribute); + } + else + { + succeeded = TryParseSpan((Span)child, attributeValueTypes, errorSink, out attribute); + } + + // Only want to track the attribute if we succeeded in parsing its corresponding Block/Span. + if (succeeded) + { + attributes[attribute.Key] = attribute.Value; + } + } + + return attributes; + } + + // This method handles cases when the attribute is a simple span attribute such as + // class="something moresomething". This does not handle complex attributes such as + // class="@myclass". Therefore the span.Content is equivalent to the entire attribute. + private static bool TryParseSpan( + Span span, + IReadOnlyDictionary attributeValueTypes, + ParserErrorSink errorSink, + out KeyValuePair attribute) + { + var afterEquals = false; + var builder = new SpanBuilder + { + CodeGenerator = span.CodeGenerator, + EditHandler = span.EditHandler, + Kind = span.Kind + }; + var htmlSymbols = span.Symbols.OfType().ToArray(); + var capturedAttributeValueStart = false; + var attributeValueStartLocation = span.Start; + var symbolOffset = 1; + string name = null; + + // Iterate down through the symbols to find the name and the start of the value. + // We subtract the symbolOffset so we don't accept an ending quote of a span. + for (var i = 0; i < htmlSymbols.Length - symbolOffset; i++) + { + var symbol = htmlSymbols[i]; + + if (afterEquals) + { + // When symbols are accepted into SpanBuilders, their locations get altered to be offset by the + // parent which is why we need to mark our start location prior to adding the symbol. + // This is needed to know the location of the attribute value start within the document. + if (!capturedAttributeValueStart) + { + capturedAttributeValueStart = true; + + attributeValueStartLocation = span.Start + symbol.Start; + } + + builder.Accept(symbol); + } + else if (name == null && symbol.Type == HtmlSymbolType.Text) + { + name = symbol.Content; + attributeValueStartLocation = SourceLocation.Advance(span.Start, name); + } + else if (symbol.Type == HtmlSymbolType.Equals) + { + // We've found an '=' symbol, this means that the coming symbols will either be a quote + // or value (in the case that the value is unquoted). + // Spaces after/before the equal symbol are not yet supported: + // https://github.com/aspnet/Razor/issues/123 + + // TODO: Handle malformed tags, if there's an '=' then there MUST be a value. + // https://github.com/aspnet/Razor/issues/104 + + SourceLocation symbolStartLocation; + + // Check for attribute start values, aka single or double quote + if (IsQuote(htmlSymbols[i + 1])) + { + // Move past the attribute start so we can accept the true value. + i++; + symbolStartLocation = htmlSymbols[i + 1].Start; + } + else + { + symbolStartLocation = symbol.Start; + + // Set the symbol offset to 0 so we don't attempt to skip an end quote that doesn't exist. + symbolOffset = 0; + } + + attributeValueStartLocation = symbolStartLocation + + span.Start + + new SourceLocation(absoluteIndex: 1, + lineIndex: 0, + characterIndex: 1); + afterEquals = true; + } + } + + // After all symbols have been added we need to set the builders start position so we do not indirectly + // modify each symbol's Start location. + builder.Start = attributeValueStartLocation; + + if (name == null) + { + errorSink.OnError(span.Start, + RazorResources.TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed, + span.Content.Length); + + attribute = default(KeyValuePair); + + return false; + } + + attribute = CreateMarkupAttribute(name, builder, attributeValueTypes); + + return true; + } + + private static bool TryParseBlock( + string tagName, + Block block, + IReadOnlyDictionary attributeValueTypes, + ParserErrorSink errorSink, + out KeyValuePair attribute) + { + // TODO: Accept more than just spans: https://github.com/aspnet/Razor/issues/96. + // The first child will only ever NOT be a Span if a user is doing something like: + // + + var childSpan = block.Children.First() as Span; + + if (childSpan == null || childSpan.Kind != SpanKind.Markup) + { + errorSink.OnError(block.Children.First().Start, + RazorResources.FormatTagHelpers_CannotHaveCSharpInTagDeclaration(tagName)); + + attribute = default(KeyValuePair); + + return false; + } + + var builder = new BlockBuilder(block); + + // If there's only 1 child it means that it's plain text inside of the attribute. + // i.e.
+ if (builder.Children.Count == 1) + { + return TryParseSpan(childSpan, attributeValueTypes, errorSink, out attribute); + } + + var textSymbol = childSpan.Symbols.FirstHtmlSymbolAs(HtmlSymbolType.Text); + var name = textSymbol != null ? textSymbol.Content : null; + + if (name == null) + { + errorSink.OnError(childSpan.Start, RazorResources.FormatTagHelpers_AttributesMustHaveAName(tagName)); + + attribute = default(KeyValuePair); + + return false; + } + + // TODO: Support no attribute values: https://github.com/aspnet/Razor/issues/220 + + // Remove first child i.e. foo=" + builder.Children.RemoveAt(0); + + // Grabbing last child to check if the attribute value is quoted. + var endNode = block.Children.Last(); + if (!endNode.IsBlock) + { + var endSpan = (Span)endNode; + var endSymbol = (HtmlSymbol)endSpan.Symbols.Last(); + + // Checking to see if it's a quoted attribute, if so we should remove end quote + if (IsQuote(endSymbol)) + { + builder.Children.RemoveAt(builder.Children.Count - 1); + } + } + + // We need to rebuild the code generators of the builder and its children (this is needed to + // ensure we don't do special attribute code generation since this is a tag helper). + block = RebuildCodeGenerators(builder.Build()); + + // If there's only 1 child at this point its value could be a simple markup span (treated differently than + // block level elements for attributes). + if (block.Children.Count() == 1) + { + var child = block.Children.First() as Span; + + if (child != null) + { + // After pulling apart the block we just have a value span. + + var spanBuilder = new SpanBuilder(child); + + attribute = CreateMarkupAttribute(name, spanBuilder, attributeValueTypes); + + return true; + } + } + + attribute = new KeyValuePair(name, block); + + return true; + } + + private static Block RebuildCodeGenerators(Block block) + { + var builder = new BlockBuilder(block); + + var isDynamic = builder.CodeGenerator is DynamicAttributeBlockCodeGenerator; + + // We don't want any attribute specific logic here, null out the block code generator. + if (isDynamic || builder.CodeGenerator is AttributeBlockCodeGenerator) + { + builder.CodeGenerator = BlockCodeGenerator.Null; + } + + for (var i = 0; i < builder.Children.Count; i++) + { + var child = builder.Children[i]; + + if (child.IsBlock) + { + // The child is a block, recurse down into the block to rebuild its children + builder.Children[i] = RebuildCodeGenerators((Block)child); + } + else + { + var childSpan = (Span)child; + ISpanCodeGenerator newCodeGenerator = null; + var literalGenerator = childSpan.CodeGenerator as LiteralAttributeCodeGenerator; + + if (literalGenerator != null) + { + if (literalGenerator.ValueGenerator == null || literalGenerator.ValueGenerator.Value == null) + { + newCodeGenerator = new MarkupCodeGenerator(); + } + else + { + newCodeGenerator = literalGenerator.ValueGenerator.Value; + } + } + else if (isDynamic && childSpan.CodeGenerator == SpanCodeGenerator.Null) + { + // Usually the dynamic code generator handles rendering the null code generators underneath + // it. This doesn't make sense in terms of tag helpers though, we need to change null code + // generators to markup code generators. + + newCodeGenerator = new MarkupCodeGenerator(); + } + + // If we have a new code generator we'll need to re-build the child + if (newCodeGenerator != null) + { + var childSpanBuilder = new SpanBuilder(childSpan) + { + CodeGenerator = newCodeGenerator + }; + + builder.Children[i] = childSpanBuilder.Build(); + } + } + } + + return builder.Build(); + } + + private static KeyValuePair CreateMarkupAttribute( + string name, + SpanBuilder builder, + IReadOnlyDictionary attributeValueTypes) + { + string attributeTypeName; + + // If the attribute was requested by the tag helper and doesn't happen to be a string then we need to treat + // its value as code. Any non-string value can be any C# value so we need to ensure the SyntaxTreeNode + // reflects that. + if (attributeValueTypes.TryGetValue(name, out attributeTypeName) && + !string.Equals(attributeTypeName, typeof(string).FullName, StringComparison.OrdinalIgnoreCase)) + { + builder.Kind = SpanKind.Code; + } + + return new KeyValuePair(name, builder.Build()); + } + + private static bool IsQuote(HtmlSymbol htmlSymbol) + { + return htmlSymbol.Type == HtmlSymbolType.DoubleQuote || + htmlSymbol.Type == HtmlSymbolType.SingleQuote; + } + + // This class is used to compare tag helper attributes by comparing only the HTML attribute name. + private class TagHelperAttributeDescriptorComparer : IEqualityComparer + { + public static readonly TagHelperAttributeDescriptorComparer Default = + new TagHelperAttributeDescriptorComparer(); + + public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY) + { + return string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.OrdinalIgnoreCase); + } + + public int GetHashCode(TagHelperAttributeDescriptor descriptor) + { + return StringComparer.OrdinalIgnoreCase.GetHashCode(descriptor.Name); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs index 5a2e5b10fa..da8b97b3b9 100644 --- a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperParseTreeRewriter.cs @@ -27,14 +27,12 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal public void Rewrite(RewritingContext context) { - RewriteTags(context.SyntaxTree); - - ValidateRewrittenSyntaxTree(context); + RewriteTags(context.SyntaxTree, context); context.SyntaxTree = _currentBlock.Build(); } - private void RewriteTags(Block input) + private void RewriteTags(Block input, RewritingContext context) { // We want to start a new block without the children from existing (we rebuild them). TrackBlock(new BlockBuilder @@ -43,6 +41,8 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal CodeGenerator = input.CodeGenerator }); + var activeTagHelpers = _tagStack.Count; + foreach (var child in input.Children) { if (child.IsBlock) @@ -51,65 +51,18 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal if (childBlock.Type == BlockType.Tag) { - // TODO: Fully handle malformed tags: https://github.com/aspnet/Razor/issues/104 - - // Get tag name of the current block (doesn't matter if it's an end or start tag) - var tagName = GetTagName(childBlock); - - // Could not determine tag name, it can't be a TagHelper, continue on and track the element. - if (tagName == null) + if (TryRewriteTagHelper(childBlock, context)) { - _currentBlock.Children.Add(child); continue; } - if (!IsEndTag(childBlock)) - { - // We're in a begin tag block - - if (IsPotentialTagHelper(tagName, childBlock)) - { - var descriptors = _provider.GetTagHelpers(tagName); - - // We could be a tag helper, but only if we have descriptors registered - if (descriptors.Any()) - { - // Found a new tag helper block - TrackTagHelperBlock(new TagHelperBlockBuilder(tagName, descriptors, childBlock)); - - // If it's a self closing block then we don't have to worry about nested children - // within the tag... complete it. - if (IsSelfClosing(childBlock)) - { - BuildCurrentlyTrackedTagHelperBlock(); - } - - continue; - } - } - } - else - { - var currentTagHelper = _tagStack.Count > 0 ? _tagStack.Peek() : null; - - // Check if it's an "end" tag helper that matches our current tag helper - if (currentTagHelper != null && - string.Equals(currentTagHelper.TagName, tagName, StringComparison.OrdinalIgnoreCase)) - { - BuildCurrentlyTrackedTagHelperBlock(); - continue; - } - - // We're in an end tag, there won't be anymore tag helpers nested. - } - - // If we get to here it means that we're a normal html tag. No need to iterate - // any deeper into the children of it because they wont be tag helpers. + // If we get to here it means that we're a normal html tag. No need to iterate any deeper into + // the children of it because they wont be tag helpers. } else { // We're not an Html tag so iterate through children recursively. - RewriteTags(childBlock); + RewriteTags(childBlock, context); continue; } } @@ -117,42 +70,141 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal // At this point the child is a Span or Block with Type BlockType.Tag that doesn't happen to be a // tag helper. - // Add the child to current block. + // Add the child to current block. _currentBlock.Children.Add(child); } + // We captured the number of active tag helpers at the start of our logic, it should be the same. If not + // it means that there are malformed tag helpers at the top of our stack. + if (activeTagHelpers != _tagStack.Count) + { + // Malformed tag helpers built here will be tag helpers that do not have end tags in the current block + // scope. Block scopes are special cases in Razor such as @

would cause an error because there's no + // matching end

tag in the template block scope and therefore doesn't make sense as a tag helper. + BuildMalformedTagHelpers(_tagStack.Count - activeTagHelpers, context); + + Debug.Assert(activeTagHelpers == _tagStack.Count); + } + BuildCurrentlyTrackedBlock(); } - private void ValidateRewrittenSyntaxTree(RewritingContext context) + private bool TryRewriteTagHelper(Block tagBlock, RewritingContext context) { - // If the blockStack still has elements in it that means there's at least one malformed TagHelper block in - // the document, that's the only way we can have a non-zero _blockStack.Count. - if (_blockStack.Count != 0) + // TODO: Fully handle malformed tags: https://github.com/aspnet/Razor/issues/104 + + // Get tag name of the current block (doesn't matter if it's an end or start tag) + var tagName = GetTagName(tagBlock); + + // Could not determine tag name, it can't be a TagHelper, continue on and track the element. + if (tagName == null) { - // We reverse the children so we can search from the back to the front for the TagHelper that is - // malformed. - var candidateChildren = _currentBlock.Children.Reverse(); - var malformedTagHelper = candidateChildren.OfType().FirstOrDefault(); + return false; + } - // If the malformed tag helper is null that means something other than a TagHelper caused the - // unbalancing of the syntax tree (should never happen). - Debug.Assert(malformedTagHelper != null); + var descriptors = Enumerable.Empty(); - // We only create a single error because we can't reasonably determine other invalid tag helpers in the - // document; having one malformed tag helper puts the document into an invalid state. - context.ErrorSink.OnError( - malformedTagHelper.Start, - RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper( - malformedTagHelper.TagName)); + if (IsPotentialTagHelper(tagName, tagBlock)) + { + descriptors = _provider.GetTagHelpers(tagName); + } - // We need to build the remaining blocks in the stack to ensure we don't return an invalid syntax tree. - do + // If there aren't any TagHelperDescriptors registered then we aren't a TagHelper + if (!descriptors.Any()) + { + return false; + } + + if (!IsEndTag(tagBlock)) + { + // We're in a begin tag helper block + + var validTagStructure = ValidTagStructure(tagName, tagBlock, context); + + var builder = TagHelperBlockRewriter.Rewrite(tagName, + validTagStructure, + tagBlock, + descriptors, + context.ErrorSink); + + // Found a new tag helper block + TrackTagHelperBlock(builder); + + // If it's a self closing block then we don't have to worry about nested children + // within the tag... complete it. + if (IsSelfClosing(tagBlock)) { BuildCurrentlyTrackedTagHelperBlock(); } - while (_blockStack.Count != 0); } + else + { + // We're in an end tag helper block. + + var tagNameScope = _tagStack.Count > 0 ? _tagStack.Peek().TagName : string.Empty; + + // Validate that our end tag helper matches the currently scoped tag helper, if not we + // need to error. + if (tagNameScope.Equals(tagName, StringComparison.OrdinalIgnoreCase)) + { + ValidTagStructure(tagName, tagBlock, context); + + BuildCurrentlyTrackedTagHelperBlock(); + } + else + { + // Current tag helper scope does not match the end tag. Attempt to recover the tag + // helper by looking up the previous tag helper scopes for a matching tag. If we + // can't recover it means there was no corresponding tag helper begin tag. + if (TryRecoverTagHelper(tagName, context)) + { + ValidTagStructure(tagName, tagBlock, context); + + // Successfully recovered, move onto the next element. + } + else + { + // Could not recover, the end tag helper has no corresponding begin tag, create + // an error based on the current childBlock. + context.ErrorSink.OnError( + tagBlock.Start, + RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper(tagName)); + + return false; + } + } + } + + return true; + } + + private static bool ValidTagStructure(string tagName, Block tag, RewritingContext context) + { + // We assume an invalid structure until we verify that the tag meets all of our "valid structure" criteria. + var invalidStructure = true; + + // No need to validate the tag end because in order to be a tag block it must start with '<'. + var tagEnd = tag.Children.Last() as Span; + + // If our tag end is not a markup span it means it's some sort of code SyntaxTreeNode (not a valid format) + if (tagEnd != null && tagEnd.Kind == SpanKind.Markup) + { + var endSymbol = tagEnd.Symbols.LastOrDefault() as HtmlSymbol; + + if (endSymbol != null && endSymbol.Type == HtmlSymbolType.CloseAngle) + { + invalidStructure = false; + } + } + + if (invalidStructure) + { + context.ErrorSink.OnError( + tag.Start, + RazorResources.FormatTagHelpersParseTreeRewriter_MissingCloseAngle(tagName)); + } + + return !invalidStructure; } private void BuildCurrentlyTrackedBlock() @@ -216,6 +268,52 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal TrackBlock(builder); } + private bool TryRecoverTagHelper(string tagName, RewritingContext context) + { + var malformedTagHelperCount = 0; + + foreach (var tag in _tagStack) + { + if (tag.TagName.Equals(tagName, StringComparison.OrdinalIgnoreCase)) + { + break; + } + + malformedTagHelperCount++; + } + + // If the malformedTagHelperCount == _tagStack.Count it means we couldn't find a begin tag for the tag + // helper, can't recover. + if (malformedTagHelperCount != _tagStack.Count) + { + BuildMalformedTagHelpers(malformedTagHelperCount, context); + + // One final build, this is the build that completes our target tag helper block which is not malformed. + BuildCurrentlyTrackedTagHelperBlock(); + + // We were able to recover + return true; + } + + // Could not recover tag helper. Aka we found a tag helper end tag without a corresponding begin tag. + return false; + } + + private void BuildMalformedTagHelpers(int count, RewritingContext context) + { + for (var i = 0; i < count; i++) + { + var malformedTagHelper = _tagStack.Peek(); + + context.ErrorSink.OnError( + malformedTagHelper.Start, + RazorResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper( + malformedTagHelper.TagName)); + + BuildCurrentlyTrackedTagHelperBlock(); + } + } + private static string GetTagName(Block tagBlock) { var child = tagBlock.Children.First(); @@ -240,9 +338,9 @@ namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal { EnsureTagBlock(beginTagBlock); - var childSpan = (Span)beginTagBlock.Children.Last(); + var childSpan = beginTagBlock.Children.Last() as Span; - return childSpan.Content.EndsWith("/>"); + return childSpan?.Content.EndsWith("/>") ?? false; } private static bool IsEndTag(Block tagBlock) diff --git a/src/Microsoft.AspNet.Razor/ParserResults.cs b/src/Microsoft.AspNet.Razor/ParserResults.cs index 97df423630..9d4b9709b0 100644 --- a/src/Microsoft.AspNet.Razor/ParserResults.cs +++ b/src/Microsoft.AspNet.Razor/ParserResults.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.TagHelpers; @@ -15,15 +17,18 @@ namespace Microsoft.AspNet.Razor /// /// Instantiates a new instance. /// - /// The Razor syntax tree. - /// s that apply to the current Razor - /// document. - /// s encountered when parsing the current Razor - /// document. + /// The for the syntax tree. + /// + /// The s that apply to the current Razor document. + /// + /// + /// The used to collect s encountered when parsing the + /// current Razor document. + /// public ParserResults([NotNull] Block document, [NotNull] IEnumerable tagHelperDescriptors, - [NotNull] IList parserErrors) - : this(parserErrors == null || parserErrors.Count == 0, document, tagHelperDescriptors, parserErrors) + [NotNull] ParserErrorSink errorSink) + : this(!errorSink.Errors.Any(), document, tagHelperDescriptors, errorSink) { } @@ -31,40 +36,50 @@ namespace Microsoft.AspNet.Razor /// Instantiates a new instance. /// /// true if parsing was successful, false otherwise. - /// The Razor syntax tree. - /// s that apply to the current Razor - /// document. - /// s encountered when parsing the current Razor - /// document. + /// The for the syntax tree. + /// + /// The s that apply to the current Razor document. + /// + /// + /// The used to collect s encountered when parsing the + /// current Razor document. + /// protected ParserResults(bool success, [NotNull] Block document, [NotNull] IEnumerable tagHelperDescriptors, - [NotNull] IList errors) + [NotNull] ParserErrorSink errorSink) { Success = success; Document = document; TagHelperDescriptors = tagHelperDescriptors; - ParserErrors = errors ?? new List(); + ErrorSink = errorSink; + ParserErrors = errorSink.Errors; } /// - /// Indicates if parsing was successful (no errors) + /// Indicates if parsing was successful (no errors). /// - public bool Success { get; private set; } + /// true if parsing was successful, false otherwise. + public bool Success { get; } /// - /// The root node in the document's syntax tree + /// The root node in the document's syntax tree. /// - public Block Document { get; private set; } + public Block Document { get; } + + /// + /// Used to aggregate s. + /// + public ParserErrorSink ErrorSink { get; } /// /// The list of errors which occurred during parsing. /// - public IList ParserErrors { get; private set; } + public IEnumerable ParserErrors { get; } /// /// The s found for the current Razor document. /// - public IEnumerable TagHelperDescriptors { get; private set; } + public IEnumerable TagHelperDescriptors { get; } } } diff --git a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs index 911da71cc8..a238ee5343 100644 --- a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs +++ b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs @@ -92,7 +92,7 @@ namespace Microsoft.AspNet.Razor /// /// The "@" character must be followed by a ":", "(", or a C# identifier. If you intended to switch to markup, use an HTML start tag, for example: - /// + /// /// @if(isLoggedIn) { /// <p>Hello, @user!</p> /// } @@ -104,7 +104,7 @@ namespace Microsoft.AspNet.Razor /// /// The "@" character must be followed by a ":", "(", or a C# identifier. If you intended to switch to markup, use an HTML start tag, for example: - /// + /// /// @if(isLoggedIn) { /// <p>Hello, @user!</p> /// } @@ -228,7 +228,7 @@ namespace Microsoft.AspNet.Razor /// /// Sections cannot be empty. The "@section" keyword must be followed by a block of markup surrounded by "{}". For example: - /// + /// /// @section Sidebar { /// <!-- Markup and text goes here --> /// } @@ -240,7 +240,7 @@ namespace Microsoft.AspNet.Razor /// /// Sections cannot be empty. The "@section" keyword must be followed by a block of markup surrounded by "{}". For example: - /// + /// /// @section Sidebar { /// <!-- Markup and text goes here --> /// } @@ -252,7 +252,7 @@ namespace Microsoft.AspNet.Razor /// /// Namespace imports and type aliases cannot be placed within code blocks. They must immediately follow an "@" character in markup. It is recommended that you put them at the top of the page, as in the following example: - /// + /// /// @using System.Drawing; /// @{ /// // OK here to use types from System.Drawing in the page. @@ -265,7 +265,7 @@ namespace Microsoft.AspNet.Razor /// /// Namespace imports and type aliases cannot be placed within code blocks. They must immediately follow an "@" character in markup. It is recommended that you put them at the top of the page, as in the following example: - /// + /// /// @using System.Drawing; /// @{ /// // OK here to use types from System.Drawing in the page. @@ -278,12 +278,12 @@ namespace Microsoft.AspNet.Razor /// /// Expected a "{0}" but found a "{1}". Block statements must be enclosed in "{{" and "}}". You cannot use single-statement control-flow statements in CSHTML pages. For example, the following is not allowed: - /// + /// /// @if(isLoggedIn) /// <p>Hello, @user</p> - /// + /// /// Instead, wrap the contents of the block in "{{}}": - /// + /// /// @if(isLoggedIn) {{ /// <p>Hello, @user</p> /// }} @@ -295,12 +295,12 @@ namespace Microsoft.AspNet.Razor /// /// Expected a "{0}" but found a "{1}". Block statements must be enclosed in "{{" and "}}". You cannot use single-statement control-flow statements in CSHTML pages. For example, the following is not allowed: - /// + /// /// @if(isLoggedIn) /// <p>Hello, @user</p> - /// + /// /// Instead, wrap the contents of the block in "{{}}": - /// + /// /// @if(isLoggedIn) {{ /// <p>Hello, @user</p> /// }} @@ -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 /// @@ -1431,7 +1335,7 @@ namespace Microsoft.AspNet.Razor } /// - /// Tag Helper attributes must have a name. + /// Tag Helper '{0}'s attributes must have names. /// internal static string TagHelpers_AttributesMustHaveAName { @@ -1439,15 +1343,15 @@ namespace Microsoft.AspNet.Razor } /// - /// Tag Helper attributes must have a name. + /// Tag Helper '{0}'s attributes must have names. /// - internal static string FormatTagHelpers_AttributesMustHaveAName() + internal static string FormatTagHelpers_AttributesMustHaveAName(object p0) { - return GetString("TagHelpers_AttributesMustHaveAName"); + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_AttributesMustHaveAName"), p0); } /// - /// Tag Helpers cannot have C# in an HTML tag element's attribute declaration area. + /// The tag helper '{0}' must not have C# in the element's attribute declaration area. /// internal static string TagHelpers_CannotHaveCSharpInTagDeclaration { @@ -1455,11 +1359,11 @@ namespace Microsoft.AspNet.Razor } /// - /// Tag Helpers cannot have C# in an HTML tag element's attribute declaration area. + /// The tag helper '{0}' must not have C# in the element's attribute declaration area. /// - internal static string FormatTagHelpers_CannotHaveCSharpInTagDeclaration() + internal static string FormatTagHelpers_CannotHaveCSharpInTagDeclaration(object p0) { - return GetString("TagHelpers_CannotHaveCSharpInTagDeclaration"); + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_CannotHaveCSharpInTagDeclaration"), p0); } /// @@ -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,6 +1430,106 @@ namespace Microsoft.AspNet.Razor return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_FoundMalformedTagHelper"), p0); } + /// + /// Missing close angle for tag helper '{0}'. + /// + internal static string TagHelpersParseTreeRewriter_MissingCloseAngle + { + get { return GetString("TagHelpersParseTreeRewriter_MissingCloseAngle"); } + } + + /// + /// Missing close angle for tag helper '{0}'. + /// + internal static string FormatTagHelpersParseTreeRewriter_MissingCloseAngle(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpersParseTreeRewriter_MissingCloseAngle"), p0); + } + + /// + /// TagHelper attributes must be welformed. + /// + internal static string TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed + { + get { return GetString("TagHelperBlockRewriter_TagHelperAttributesMustBeWelformed"); } + } + + /// + /// TagHelper attributes must be welformed. + /// + internal static string FormatTagHelperBlockRewriter_TagHelperAttributesMustBeWelformed() + { + 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 3e25ad9efd..748e14ef5d 100644 --- a/src/Microsoft.AspNet.Razor/RazorResources.resx +++ b/src/Microsoft.AspNet.Razor/RazorResources.resx @@ -1,17 +1,17 @@ - @@ -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 @@ -404,17 +385,14 @@ Instead, wrap the contents of the block in "{{}}": Section blocks ("{0}") cannot be nested. Only one level of section blocks are allowed. - Tag Helper attributes must have a name. + Tag Helper '{0}'s attributes must have names. - Tag Helpers cannot have C# in an HTML tag element's attribute declaration area. + The tag helper '{0}' must not have C# in the element's attribute declaration area. 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,4 +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 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/RazorTemplateEngine.cs b/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs index 40e10a0474..1e38b4eb9e 100644 --- a/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs +++ b/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs @@ -252,7 +252,7 @@ namespace Microsoft.AspNet.Razor generator.DesignTimeMode = Host.DesignTimeMode; generator.Visit(results); - var codeBuilderContext = new CodeBuilderContext(generator.Context); + var codeBuilderContext = new CodeBuilderContext(generator.Context, results.ErrorSink); codeBuilderContext.Checksum = checksum; var builder = CreateCodeBuilder(codeBuilderContext); var builderResult = builder.Build(); diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs b/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs deleted file mode 100644 index b98be35f07..0000000000 --- a/src/Microsoft.AspNet.Razor/TagHelpers/ContentBehavior.cs +++ /dev/null @@ -1,43 +0,0 @@ -// 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. - -namespace Microsoft.AspNet.Razor.TagHelpers -{ - /// - /// Defines how a tag helper will utilize its inner HTML. - /// - public enum ContentBehavior - { - /// - /// Indicates that a tag helper will not modify its content in any way. This is the default - /// . - /// - /// Children of the current tag helper will execute after the current tag helper. - None, - - /// - /// Indicates the tag helper's content should be appended to what its children generate. - /// - /// Children of the current tag helper will execute before the current tag helper. - Append, - - /// - /// Indicates that the tag helper will modify its HTML content. Therefore this - /// enables the tag helper to examine the content its children generate. - /// - /// Children of the current tag helper will execute before the current tag helper. - Modify, - - /// - /// Indicates the tag helper's content should be prepended to what its children generate. - /// - /// Children of the current tag helper will execute after the current tag helper. - Prepend, - - /// - /// Indicates the tag helper's content should replace the HTML its children generate. - /// - /// Children of the current tag helper will not execute. - Replace, - } -} \ 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/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs index 97ae02ba22..77c080054f 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptor.cs @@ -16,12 +16,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// internal TagHelperDescriptor([NotNull] string tagName, [NotNull] string typeName, - [NotNull] string assemblyName, - ContentBehavior contentBehavior) + [NotNull] string assemblyName) : this(tagName, typeName, assemblyName, - contentBehavior, Enumerable.Empty()) { } @@ -34,21 +32,17 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// which applies to every HTML tag. /// The full name of the tag helper class. /// The name of the assembly containing the tag helper class. - /// The - /// of the tag helper. /// /// The s to request from the HTML tag. /// public TagHelperDescriptor([NotNull] string tagName, [NotNull] string typeName, [NotNull] string assemblyName, - ContentBehavior contentBehavior, [NotNull] IEnumerable attributes) { TagName = tagName; TypeName = typeName; AssemblyName = assemblyName; - ContentBehavior = contentBehavior; Attributes = new List(attributes); } @@ -67,11 +61,6 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// public string AssemblyName { get; private set; } - /// - /// The of the tag helper. - /// - public ContentBehavior ContentBehavior { get; private set; } - /// /// The list of attributes the tag helper expects. /// diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs index 17e25079b8..0f79c05154 100644 --- a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorComparer.cs @@ -29,15 +29,13 @@ namespace Microsoft.AspNet.Razor.TagHelpers /// false otherwise. /// /// Determines equality based on , - /// , and - /// . + /// and . /// public bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY) { return string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) && string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) && - string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) && - descriptorX.ContentBehavior == descriptorY.ContentBehavior; + string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal); } /// @@ -51,7 +49,6 @@ namespace Microsoft.AspNet.Razor.TagHelpers .Add(descriptor.TagName, StringComparer.OrdinalIgnoreCase) .Add(descriptor.TypeName, StringComparer.Ordinal) .Add(descriptor.AssemblyName, StringComparer.Ordinal) - .Add(descriptor.ContentBehavior) .CombinedHash; } } diff --git a/src/Microsoft.AspNet.Razor/project.json b/src/Microsoft.AspNet.Razor/project.json index 2c9965ddf8..43284e0cfa 100644 --- a/src/Microsoft.AspNet.Razor/project.json +++ b/src/Microsoft.AspNet.Razor/project.json @@ -9,12 +9,13 @@ "System.Collections": "4.0.10-beta-*", "System.Diagnostics.Debug": "4.0.10-beta-*", "System.Diagnostics.Tools": "4.0.0-beta-*", + "System.IO": "4.0.10-beta-*", "System.IO.FileSystem": "4.0.0-beta-*", "System.Linq": "4.0.0-beta-*", "System.Reflection": "4.0.10-beta-*", "System.Resources.ResourceManager": "4.0.0-beta-*", "System.Runtime.Extensions": "4.0.10-beta-*", - "System.Threading": "4.0.0-beta-*", + "System.Threading": "4.0.10-beta-*", "System.Threading.Tasks": "4.0.10-beta-*", "System.Threading.Thread": "4.0.0-beta-*", "System.Security.Cryptography.Hashing.Algorithms": "4.0.0-beta-*" diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs index 5a00ff6e65..2897de31cb 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/CompleteTagHelperDescriptorComparer.cs @@ -1,6 +1,7 @@ // 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 System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.Razor.TagHelpers; @@ -20,6 +21,8 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers bool IEqualityComparer.Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY) { return base.Equals(descriptorX, descriptorY) && + // Tests should be exact casing + string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.Ordinal) && descriptorX.Attributes.SequenceEqual(descriptorY.Attributes, CompleteTagHelperAttributeDescriptorComparer.Default); } @@ -43,17 +46,17 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY) { - return descriptorX.Name == descriptorY.Name && - descriptorX.PropertyName == descriptorY.PropertyName && - descriptorX.TypeName == descriptorY.TypeName; + return string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.Ordinal) && + string.Equals(descriptorX.PropertyName, descriptorY.PropertyName, StringComparison.Ordinal) && + string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal); } public int GetHashCode(TagHelperAttributeDescriptor descriptor) { return HashCodeCombiner.Start() - .Add(descriptor.Name) - .Add(descriptor.PropertyName) - .Add(descriptor.TypeName) + .Add(descriptor.Name, StringComparer.Ordinal) + .Add(descriptor.PropertyName, StringComparer.Ordinal) + .Add(descriptor.TypeName, StringComparer.Ordinal) .CombinedHash; } } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs index 072d68b323..fa229c4bc4 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorFactoryTest.cs @@ -1,6 +1,7 @@ // 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 System; using System.Reflection; using Microsoft.AspNet.Razor.TagHelpers; using Xunit; @@ -12,6 +13,42 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers private static readonly string AssemblyName = typeof(TagHelperDescriptorFactoryTest).GetTypeInfo().Assembly.GetName().Name; + public static TheoryData HtmlCaseData + { + get + { + // tagHelperType, expectedTagName, expectedAttributeName + return new TheoryData + { + { typeof(SingleAttributeTagHelper), "single-attribute", "int-attribute" }, + { typeof(ALLCAPSTAGHELPER), "allcaps", "allcapsattribute" }, + { typeof(CAPSOnOUTSIDETagHelper), "caps-on-outside", "caps-on-outsideattribute" }, + { typeof(capsONInsideTagHelper), "caps-on-inside", "caps-on-insideattribute" }, + { typeof(One1Two2Three3TagHelper), "one1-two2-three3", "one1-two2-three3-attribute" }, + { typeof(ONE1TWO2THREE3TagHelper), "one1two2three3", "one1two2three3-attribute" }, + { typeof(First_Second_ThirdHiTagHelper), "first_second_third-hi", "first_second_third-attribute" }, + { typeof(UNSuffixedCLASS), "un-suffixed-class", "un-suffixed-attribute" }, + }; + } + } + + [Theory] + [MemberData(nameof(HtmlCaseData))] + public void CreateDescriptor_HtmlCasesTagNameAndAttributeName( + Type tagHelperType, + string expectedTagName, + string expectedAttributeName) + { + // Arrange & Act + var descriptors = TagHelperDescriptorFactory.CreateDescriptors(tagHelperType); + + // Assert + var descriptor = Assert.Single(descriptors); + Assert.Equal(expectedTagName, descriptor.TagName, StringComparer.Ordinal); + var attributeDescriptor = Assert.Single(descriptor.Attributes); + Assert.Equal(expectedAttributeName, attributeDescriptor.Name); + } + [Fact] public void CreateDescriptor_OverridesAttributeNameFromAttribute() { @@ -22,10 +59,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers nameof(OverriddenAttributeTagHelper.ValidAttribute2)); var expectedDescriptors = new[] { new TagHelperDescriptor( - "OverriddenAttribute", + "overridden-attribute", typeof(OverriddenAttributeTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { new TagHelperAttributeDescriptor("SomethingElse", validProperty1), new TagHelperAttributeDescriptor("Something-Else", validProperty2) @@ -49,12 +85,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute2)); var expectedDescriptors = new[] { new TagHelperDescriptor( - "InheritedOverriddenAttribute", + "inherited-overridden-attribute", typeof(InheritedOverriddenAttributeTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(InheritedOverriddenAttributeTagHelper.ValidAttribute1), + new TagHelperAttributeDescriptor("valid-attribute1", validProperty1), new TagHelperAttributeDescriptor("Something-Else", validProperty2) }) @@ -77,10 +112,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers nameof(InheritedNotOverriddenAttributeTagHelper.ValidAttribute2)); var expectedDescriptors = new[] { new TagHelperDescriptor( - "InheritedNotOverriddenAttribute", + "inherited-not-overridden-attribute", typeof(InheritedNotOverriddenAttributeTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { new TagHelperAttributeDescriptor("SomethingElse", validProperty1), new TagHelperAttributeDescriptor("Something-Else", validProperty2) @@ -100,7 +134,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Arrange var objectAssemblyName = typeof(object).GetTypeInfo().Assembly.GetName().Name; var expectedDescriptor = - new TagHelperDescriptor("Object", "System.Object", objectAssemblyName, ContentBehavior.None); + new TagHelperDescriptor("object", "System.Object", objectAssemblyName); // Act var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(object)); @@ -117,12 +151,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var intProperty = typeof(InheritedSingleAttributeTagHelper).GetProperty( nameof(InheritedSingleAttributeTagHelper.IntAttribute)); var expectedDescriptor = new TagHelperDescriptor( - "InheritedSingleAttribute", + "inherited-single-attribute", typeof(InheritedSingleAttributeTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(InheritedSingleAttributeTagHelper.IntAttribute), intProperty) + new TagHelperAttributeDescriptor("int-attribute", intProperty) }); // Act @@ -139,12 +172,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Arrange var intProperty = typeof(SingleAttributeTagHelper).GetProperty(nameof(SingleAttributeTagHelper.IntAttribute)); var expectedDescriptor = new TagHelperDescriptor( - "SingleAttribute", + "single-attribute", typeof(SingleAttributeTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(SingleAttributeTagHelper.IntAttribute), intProperty) + new TagHelperAttributeDescriptor("int-attribute", intProperty) }); // Act @@ -162,12 +194,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var validProperty = typeof(MissingAccessorTagHelper).GetProperty( nameof(MissingAccessorTagHelper.ValidAttribute)); var expectedDescriptor = new TagHelperDescriptor( - "MissingAccessor", + "missing-accessor", typeof(MissingAccessorTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(MissingAccessorTagHelper.ValidAttribute), validProperty) + new TagHelperAttributeDescriptor("valid-attribute", validProperty) }); // Act @@ -185,13 +216,12 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var validProperty = typeof(PrivateAccessorTagHelper).GetProperty( nameof(PrivateAccessorTagHelper.ValidAttribute)); var expectedDescriptor = new TagHelperDescriptor( - "PrivateAccessor", + "private-accessor", typeof(PrivateAccessorTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { new TagHelperAttributeDescriptor( - nameof(PrivateAccessorTagHelper.ValidAttribute), validProperty) + "valid-attribute", validProperty) }); // Act @@ -202,43 +232,6 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); } - [Fact] - public void CreateDescriptor_ResolvesCustomContentBehavior() - { - // Arrange - var expectedDescriptor = new TagHelperDescriptor( - "CustomContentBehavior", - typeof(CustomContentBehaviorTagHelper).FullName, - AssemblyName, - ContentBehavior.Append); - - // Act - var descriptors = TagHelperDescriptorFactory.CreateDescriptors(typeof(CustomContentBehaviorTagHelper)); - - // Assert - var descriptor = Assert.Single(descriptors); - Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); - } - - [Fact] - public void CreateDescriptor_DoesNotResolveInheritedCustomContentBehavior() - { - // Arrange - var expectedDescriptor = new TagHelperDescriptor( - "InheritedCustomContentBehavior", - typeof(InheritedCustomContentBehaviorTagHelper).FullName, - AssemblyName, - ContentBehavior.None); - - // Act - var descriptors = TagHelperDescriptorFactory.CreateDescriptors( - typeof(InheritedCustomContentBehaviorTagHelper)); - - // Assert - var descriptor = Assert.Single(descriptors); - Assert.Equal(expectedDescriptor, descriptor, CompleteTagHelperDescriptorComparer.Default); - } - [Fact] public void CreateDescriptor_ResolvesMultipleTagHelperDescriptorsFromSingleType() { @@ -249,17 +242,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers "div", typeof(MultiTagTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(MultiTagTagHelper.ValidAttribute), validProp) + new TagHelperAttributeDescriptor("valid-attribute", validProp) }), new TagHelperDescriptor( "p", typeof(MultiTagTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(MultiTagTagHelper.ValidAttribute), validProp) + new TagHelperAttributeDescriptor("valid-attribute", validProp) }) }; @@ -276,12 +267,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Arrange var validProp = typeof(InheritedMultiTagTagHelper).GetProperty(nameof(InheritedMultiTagTagHelper.ValidAttribute)); var expectedDescriptor = new TagHelperDescriptor( - "InheritedMultiTag", + "inherited-multi-tag", typeof(InheritedMultiTagTagHelper).FullName, AssemblyName, - ContentBehavior.None, new[] { - new TagHelperAttributeDescriptor(nameof(InheritedMultiTagTagHelper.ValidAttribute), validProp) + new TagHelperAttributeDescriptor("valid-attribute", validProp) }); // Act @@ -300,13 +290,11 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers new TagHelperDescriptor( "p", typeof(DuplicateTagNameTagHelper).FullName, - AssemblyName, - ContentBehavior.None), + AssemblyName), new TagHelperDescriptor( "div", typeof(DuplicateTagNameTagHelper).FullName, - AssemblyName, - ContentBehavior.None) + AssemblyName) }; // Act @@ -323,8 +311,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var expectedDescriptors = new[] { new TagHelperDescriptor("data-condition", typeof(OverrideNameTagHelper).FullName, - AssemblyName, - ContentBehavior.None), + AssemblyName), }; // Act @@ -342,18 +329,15 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers new TagHelperDescriptor( "span", typeof(MultipleAttributeTagHelper).FullName, - AssemblyName, - ContentBehavior.None), + AssemblyName), new TagHelperDescriptor( "p", typeof(MultipleAttributeTagHelper).FullName, - AssemblyName, - ContentBehavior.None), + AssemblyName), new TagHelperDescriptor( "div", typeof(MultipleAttributeTagHelper).FullName, - AssemblyName, - ContentBehavior.None) + AssemblyName) }; // Act @@ -363,16 +347,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Equal(expectedDescriptors, descriptors, CompleteTagHelperDescriptorComparer.Default); } - [ContentBehavior(ContentBehavior.Append)] - private class CustomContentBehaviorTagHelper - { - } - - private class InheritedCustomContentBehaviorTagHelper : CustomContentBehaviorTagHelper - { - } - - [TagName("p", "div")] + [HtmlElementName("p", "div")] private class MultiTagTagHelper { public string ValidAttribute { get; set; } @@ -382,18 +357,18 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { } - [TagName("p", "p", "div", "div")] + [HtmlElementName("p", "p", "div", "div")] private class DuplicateTagNameTagHelper { } - [TagName("data-condition")] + [HtmlElementName("data-condition")] private class OverrideNameTagHelper { } - [TagName("span")] - [TagName("div", "p")] + [HtmlElementName("span")] + [HtmlElementName("div", "p")] private class MultipleAttributeTagHelper { } @@ -419,5 +394,41 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers private class InheritedNotOverriddenAttributeTagHelper : OverriddenAttributeTagHelper { } + + private class ALLCAPSTAGHELPER : TagHelper + { + public int ALLCAPSATTRIBUTE { get; set; } + } + + private class CAPSOnOUTSIDETagHelper : TagHelper + { + public int CAPSOnOUTSIDEATTRIBUTE { get; set; } + } + + private class capsONInsideTagHelper : TagHelper + { + public int capsONInsideattribute { get; set; } + } + + private class One1Two2Three3TagHelper : TagHelper + { + public int One1Two2Three3Attribute { get; set; } + } + + private class ONE1TWO2THREE3TagHelper : TagHelper + { + public int ONE1TWO2THREE3Attribute { get; set; } + } + + private class First_Second_ThirdHiTagHelper : TagHelper + { + public int First_Second_ThirdAttribute { get; set; } + } + + private class UNSuffixedCLASS : TagHelper + { + public int UNSuffixedATTRIBUTE { get; set; } + + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs index c2eba5d79d..c847a6c8fa 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperDescriptorResolverTest.cs @@ -25,10 +25,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { get { - return new TagHelperDescriptor("Valid_Plain", + return new TagHelperDescriptor("valid_plain", Valid_PlainTagHelperType.FullName, - AssemblyName, - ContentBehavior.None); + AssemblyName); } } @@ -36,10 +35,9 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { get { - return new TagHelperDescriptor("Valid_Inherited", + return new TagHelperDescriptor("valid_inherited", Valid_InheritedTagHelperType.FullName, - AssemblyName, - ContentBehavior.None); + AssemblyName); } } @@ -75,8 +73,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var stringTagHelperDescriptor = new TagHelperDescriptor("string", "System.String", - assemblyB, - ContentBehavior.None); + assemblyB); return new TheoryData>, // descriptorAssemblyLookups IEnumerable, // directiveDescriptors @@ -230,8 +227,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers var stringTagHelperDescriptor = new TagHelperDescriptor("string", "System.String", - assemblyB, - ContentBehavior.None); + assemblyB); return new TheoryData>, // descriptorAssemblyLookups IEnumerable> // directiveDescriptors @@ -358,7 +354,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Arrange var tagHelperTypeResolver = new TestTagHelperTypeResolver(TestableTagHelpers) { - OnGetLibraryDefinedTypes = (assemblyName) => + OnGetExportedTypes = (assemblyName) => { Assert.Equal("MyAssembly", assemblyName.Name); } @@ -523,7 +519,7 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers _lookupValues = lookupValues; } - internal override IEnumerable GetLibraryDefinedTypes(AssemblyName assemblyName) + protected override IEnumerable GetExportedTypes(AssemblyName assemblyName) { IEnumerable types; diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs index 1b51c9ba85..909eae7a5e 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperExecutionContextTest.cs @@ -1,14 +1,76 @@ // 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 System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Tasks; using Xunit; namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { public class TagHelperExecutionContextTest { + [Fact] + public async Task GetChildContentAsync_CachesValue() + { + // Arrange + var writer = new StringWriter(); + var expectedContent = string.Empty; + var executionContext = new TagHelperExecutionContext( + "p", + uniqueId: string.Empty, + executeChildContentAsync: () => + { + if (string.IsNullOrEmpty(expectedContent)) + { + expectedContent = "Hello from child content: " + Guid.NewGuid().ToString(); + } + + writer.Write(expectedContent); + + return Task.FromResult(result: true); + }, + startWritingScope: () => { }, + endWritingScope: () => writer); + + // Act + var content1 = await executionContext.GetChildContentAsync(); + var content2 = await executionContext.GetChildContentAsync(); + + // Assert + Assert.Same(content1, content2); + Assert.Equal(expectedContent, content1); + Assert.Equal(expectedContent, content2); + } + + [Fact] + public async Task ExecuteChildContentAsync_IsNotMemoized() + { + // Arrange + var childContentExecutionCount = 0; + var executionContext = new TagHelperExecutionContext( + "p", + uniqueId: string.Empty, + executeChildContentAsync: () => + { + childContentExecutionCount++; + + return Task.FromResult(result: true); + }, + startWritingScope: () => { }, + endWritingScope: () => new StringWriter()); + + // Act + await executionContext.ExecuteChildContentAsync(); + await executionContext.ExecuteChildContentAsync(); + await executionContext.ExecuteChildContentAsync(); + + // Assert + Assert.Equal(3, childContentExecutionCount); + } + public static TheoryData DictionaryCaseTestingData { get diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs index 34a34a3659..343853447e 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs @@ -9,39 +9,65 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public class TagHelperOutputTest { [Fact] - public void TagName_CannotSetToNullInCtor() + public void TagName_CanSetToNullInCtor() { // Arrange & Act var tagHelperOutput = new TagHelperOutput(null); // Assert - Assert.Empty(tagHelperOutput.TagName); + Assert.Null(tagHelperOutput.TagName); } [Fact] - public void TagName_CannotSetToNull() + public void TagName_CanSetToNull() { - // Arrange - var tagHelperOutput = new TagHelperOutput("p"); - - // Act - tagHelperOutput.TagName = null; + // Arrange & Act + var tagHelperOutput = new TagHelperOutput("p") + { + TagName = null + }; // Assert - Assert.Empty(tagHelperOutput.TagName); + Assert.Null(tagHelperOutput.TagName); } [Fact] - public void Content_CannotSetToNull() + public void Content_CanSetToNull() { - // Arrange - var tagHelperOutput = new TagHelperOutput("p"); - - // Act - tagHelperOutput.Content = null; + // Arrange & Act + var tagHelperOutput = new TagHelperOutput("p") + { + Content = null + }; // Assert - Assert.Empty(tagHelperOutput.Content); + Assert.Null(tagHelperOutput.Content); + } + + [Fact] + public void PreContent_CanSetToNull() + { + // Arrange & Act + var tagHelperOutput = new TagHelperOutput("p") + { + PreContent = null + }; + + // Assert + Assert.Null(tagHelperOutput.PreContent); + } + + [Fact] + public void PostContent_CanSetToNull() + { + // Arrange & Act + var tagHelperOutput = new TagHelperOutput("p") + { + PostContent = null + }; + + // Assert + Assert.Null(tagHelperOutput.PostContent); } [Fact] @@ -119,9 +145,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers { { "class", "btn" }, { "something", " spaced " } - }); - - tagHelperOutput.SelfClosing = true; + }) + { + SelfClosing = true + }; // Act var output = tagHelperOutput.GenerateStartTag(); @@ -135,9 +162,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public void GenerateEndTag_ReturnsNothingIfWhitespaceTagName() { // Arrange - var tagHelperOutput = new TagHelperOutput(" "); ; - - tagHelperOutput.Content = "Hello World"; + var tagHelperOutput = new TagHelperOutput(" ") + { + Content = "Hello World" + }; // Act var output = tagHelperOutput.GenerateEndTag(); @@ -146,13 +174,47 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Empty(output); } + [Fact] + public void GeneratePreContent_ReturnsPreContent() + { + // Arrange + var tagHelperOutput = new TagHelperOutput("p") + { + PreContent = "Hello World" + }; + + // Act + var output = tagHelperOutput.GeneratePreContent(); + + // Assert + Assert.Equal("Hello World", output); + } + + [Fact] + public void GeneratePreContent_ReturnsNothingIfSelfClosing() + { + // Arrange + var tagHelperOutput = new TagHelperOutput("p") + { + SelfClosing = true, + PreContent = "Hello World" + }; + + // Act + var output = tagHelperOutput.GeneratePreContent(); + + // Assert + Assert.Empty(output); + } + [Fact] public void GenerateContent_ReturnsContent() { // Arrange - var tagHelperOutput = new TagHelperOutput("p"); - - tagHelperOutput.Content = "Hello World"; + var tagHelperOutput = new TagHelperOutput("p") + { + Content = "Hello World" + }; // Act var output = tagHelperOutput.GenerateContent(); @@ -168,11 +230,10 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers // Arrange var tagHelperOutput = new TagHelperOutput("p") { - SelfClosing = true + SelfClosing = true, + Content = "Hello World" }; - tagHelperOutput.Content = "Hello World"; - // Act var output = tagHelperOutput.GenerateContent(); @@ -180,6 +241,39 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Empty(output); } + [Fact] + public void GeneratePostContent_ReturnsPostContent() + { + // Arrange + var tagHelperOutput = new TagHelperOutput("p") + { + PostContent = "Hello World" + }; + + // Act + var output = tagHelperOutput.GeneratePostContent(); + + // Assert + Assert.Equal("Hello World", output); + } + + [Fact] + public void GeneratePostContent_ReturnsNothingIfSelfClosing() + { + // Arrange + var tagHelperOutput = new TagHelperOutput("p") + { + SelfClosing = true, + PostContent = "Hello World" + }; + + // Act + var output = tagHelperOutput.GeneratePostContent(); + + // Assert + Assert.Empty(output); + } + [Fact] public void GenerateEndTag_ReturnsEndTag() { @@ -209,6 +303,54 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Empty(output); } + [Fact] + public void SuppressOutput_Sets_TagName_Content_PreContent_PostContent_ToNull() + { + // Arrange + var tagHelperOutput = new TagHelperOutput("p") + { + PreContent = "Pre Content", + Content = "Content", + PostContent = "Post Content" + }; + + // Act + tagHelperOutput.SuppressOutput(); + + // Assert + Assert.Null(tagHelperOutput.TagName); + Assert.Null(tagHelperOutput.PreContent); + Assert.Null(tagHelperOutput.Content); + Assert.Null(tagHelperOutput.PostContent); + } + + [Fact] + public void SuppressOutput_PreventsTagOutput() + { + // Arrange + var tagHelperOutput = new TagHelperOutput("p", + attributes: new Dictionary + { + { "class", "btn" }, + { "something", " spaced " } + }) + { + PreContent = "Pre Content", + Content = "Content", + PostContent = "Post Content" + }; + + // Act + tagHelperOutput.SuppressOutput(); + + // Assert + Assert.Empty(tagHelperOutput.GenerateStartTag()); + Assert.Null(tagHelperOutput.GeneratePreContent()); + Assert.Null(tagHelperOutput.GenerateContent()); + Assert.Null(tagHelperOutput.GeneratePostContent()); + Assert.Empty(tagHelperOutput.GenerateEndTag()); + } + [Theory] [InlineData("class", "ClASs")] [InlineData("CLaSs", "class")] diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperRunnerTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperRunnerTest.cs index 7e09f88404..d8e7d829a9 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperRunnerTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperRunnerTest.cs @@ -66,23 +66,6 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers Assert.Equal("True", output.Attributes["foo"]); } - [Fact] - public async Task RunAsync_WithContentSetsOutputsContent() - { - // Arrange - var runner = new TagHelperRunner(); - var executionContext = new TagHelperExecutionContext("p"); - var tagHelper = new ExecutableTagHelper(); - var contentWriter = new StringWriter(new StringBuilder("Hello World")); - - // Act - executionContext.Add(tagHelper); - var output = await runner.RunAsync(executionContext, contentWriter); - - // Assert - Assert.Equal(output.Content, "Hello World"); - } - private class ExecutableTagHelper : TagHelper { public bool Processed { get; set; } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperScopeManagerTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperScopeManagerTest.cs index 7697f52b72..3fc94323c4 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperScopeManagerTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperScopeManagerTest.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.IO; +using System.Threading.Tasks; using Microsoft.AspNet.Razor.Runtime.TagHelpers; using Xunit; @@ -9,6 +11,11 @@ namespace Microsoft.AspNet.Razor.Runtime.Test.TagHelpers { public class TagHelperScopeManagerTest { + private static readonly Action DefaultStartWritingScope = () => { }; + private static readonly Func DefaultEndWritingScope = () => new StringWriter(); + private static readonly Func DefaultExecuteChildContentAsync = + async () => await Task.FromResult(result: true); + [Fact] public void Begin_CreatesContextWithAppropriateTagName() { @@ -16,7 +23,11 @@ namespace Microsoft.AspNet.Razor.Runtime.Test.TagHelpers var scopeManager = new TagHelperScopeManager(); // Act - var executionContext = scopeManager.Begin("p"); + var executionContext = scopeManager.Begin("p", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); // Assert Assert.Equal("p", executionContext.TagName); @@ -29,8 +40,16 @@ namespace Microsoft.AspNet.Razor.Runtime.Test.TagHelpers var scopeManager = new TagHelperScopeManager(); // Act - var executionContext = scopeManager.Begin("p"); - executionContext = scopeManager.Begin("div"); + var executionContext = scopeManager.Begin("p", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); + executionContext = scopeManager.Begin("div", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); // Assert Assert.Equal("div", executionContext.TagName); @@ -43,8 +62,16 @@ namespace Microsoft.AspNet.Razor.Runtime.Test.TagHelpers var scopeManager = new TagHelperScopeManager(); // Act - var executionContext = scopeManager.Begin("p"); - executionContext = scopeManager.Begin("div"); + var executionContext = scopeManager.Begin("p", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); + executionContext = scopeManager.Begin("div", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); executionContext = scopeManager.End(); // Assert @@ -58,8 +85,16 @@ namespace Microsoft.AspNet.Razor.Runtime.Test.TagHelpers var scopeManager = new TagHelperScopeManager(); // Act - var executionContext = scopeManager.Begin("p"); - executionContext = scopeManager.Begin("div"); + var executionContext = scopeManager.Begin("p", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); + executionContext = scopeManager.Begin("div", + string.Empty, + DefaultExecuteChildContentAsync, + DefaultStartWritingScope, + DefaultEndWritingScope); executionContext = scopeManager.End(); executionContext = scopeManager.End(); @@ -85,7 +120,6 @@ namespace Microsoft.AspNet.Razor.Runtime.Test.TagHelpers }); Assert.Equal(expectedError, ex.Message); - } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs index a48c06e8b5..185406936d 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/TagHelpers/TagHelperTypeResolverTest.cs @@ -114,14 +114,14 @@ namespace Microsoft.AspNet.Razor.Runtime.TagHelpers public TestTagHelperTypeResolver(IEnumerable assemblyTypes) { _assemblyTypeInfos = assemblyTypes.Select(type => type.GetTypeInfo()); - OnGetLibraryDefinedTypes = (_) => { }; + OnGetExportedTypes = (_) => { }; } - public Action OnGetLibraryDefinedTypes { get; set; } + public Action OnGetExportedTypes { get; set; } - internal override IEnumerable GetLibraryDefinedTypes(AssemblyName assemblyName) + protected override IEnumerable GetExportedTypes(AssemblyName assemblyName) { - OnGetLibraryDefinedTypes(assemblyName); + OnGetExportedTypes(assemblyName); return _assemblyTypeInfos; } diff --git a/test/Microsoft.AspNet.Razor.Runtime.Test/project.json b/test/Microsoft.AspNet.Razor.Runtime.Test/project.json index 5d95b31511..5b5f0fba0b 100644 --- a/test/Microsoft.AspNet.Razor.Runtime.Test/project.json +++ b/test/Microsoft.AspNet.Razor.Runtime.Test/project.json @@ -3,15 +3,16 @@ "dependencies": { "Microsoft.AspNet.Razor.Runtime": "4.0.0-*", "Microsoft.AspNet.Testing": "1.0.0-*", - "Xunit.KRunner": "1.0.0-*" + "xunit.runner.kre": "1.0.0-*" }, "commands": { - "test": "Xunit.KRunner" + "test": "xunit.runner.kre" }, "frameworks": { "aspnet50": { }, "aspnetcore50": { "dependencies": { + "System.Reflection.TypeExtensions": "4.0.0-beta-*", "System.Runtime.Extensions": "4.0.10-beta-*" } } diff --git a/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs b/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs index 44a734d091..a7475f3108 100644 --- a/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/CSharpRazorCodeLanguageTest.cs @@ -54,7 +54,8 @@ namespace Microsoft.AspNet.Razor.Test "myclass", "myns", string.Empty, - shouldGenerateLinePragmas: false); + shouldGenerateLinePragmas: false, + errorSink: new ParserErrorSink()); // Act var generator = language.CreateCodeBuilder(codeBuilderContext); diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs index b617f48724..33516b6058 100644 --- a/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs +++ b/test/Microsoft.AspNet.Razor.Test/Framework/ParserTestBase.cs @@ -434,28 +434,30 @@ namespace Microsoft.AspNet.Razor.Test.Framework collector.AddError("{0} - FAILED :: Actual: << Null >>", expected); } - public static void EvaluateRazorErrors(IList actualErrors, IList expectedErrors) + public static void EvaluateRazorErrors(IEnumerable actualErrors, IList expectedErrors) { + var realCount = actualErrors.Count(); + // Evaluate the errors if (expectedErrors == null || expectedErrors.Count == 0) { - Assert.True(actualErrors.Count == 0, + Assert.True(realCount == 0, String.Format("Expected that no errors would be raised, but the following errors were:\r\n{0}", FormatErrors(actualErrors))); } else { - Assert.True(expectedErrors.Count == actualErrors.Count, + Assert.True(expectedErrors.Count == realCount, String.Format("Expected that {0} errors would be raised, but {1} errors were.\r\nExpected Errors: \r\n{2}\r\nActual Errors: \r\n{3}", expectedErrors.Count, - actualErrors.Count, + realCount, FormatErrors(expectedErrors), FormatErrors(actualErrors))); - Assert.Equal(expectedErrors.ToArray(), actualErrors.ToArray()); + Assert.Equal(expectedErrors, actualErrors); } WriteTraceLine("Expected Errors were raised:\r\n{0}", FormatErrors(expectedErrors)); } - public static string FormatErrors(IList errors) + public static string FormatErrors(IEnumerable errors) { if (errors == null) { diff --git a/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs b/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs index 081a9a518c..6cb98fc075 100644 --- a/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs +++ b/test/Microsoft.AspNet.Razor.Test/Framework/TestSpanBuilder.cs @@ -300,11 +300,6 @@ namespace Microsoft.AspNet.Razor.Test.Framework return _self.With(new SetBaseTypeCodeGenerator(baseType)); } - public SpanConstructor AsRazorDirectiveAttribute(string key, string value) - { - return _self.With(new RazorDirectiveAttributeCodeGenerator(key, value)); - } - public SpanConstructor AsAddTagHelper(string lookupText) { return _self.With( diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs index ef5c9e36de..a706e68e50 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingTest.cs @@ -26,21 +26,18 @@ namespace Microsoft.AspNet.Razor.Test.Generator new TagHelperDescriptor("p", "PTagHelper", "SomeAssembly", - ContentBehavior.None, new [] { new TagHelperAttributeDescriptor("age", pAgePropertyInfo) }), new TagHelperDescriptor("input", "InputTagHelper", "SomeAssembly", - ContentBehavior.None, new TagHelperAttributeDescriptor[] { new TagHelperAttributeDescriptor("type", inputTypePropertyInfo) }), new TagHelperDescriptor("input", "InputTagHelper2", "SomeAssembly", - ContentBehavior.None, new TagHelperAttributeDescriptor[] { new TagHelperAttributeDescriptor("type", inputTypePropertyInfo), new TagHelperAttributeDescriptor("checked", checkedPropertyInfo) @@ -49,21 +46,6 @@ namespace Microsoft.AspNet.Razor.Test.Generator } } - private static IEnumerable ContentBehaviorTagHelperDescriptors - { - get - { - return new[] - { - new TagHelperDescriptor("modify", "ModifyTagHelper", "SomeAssembly", ContentBehavior.Modify), - new TagHelperDescriptor("none", "NoneTagHelper", "SomeAssembly", ContentBehavior.None), - new TagHelperDescriptor("append", "AppendTagHelper", "SomeAssembly", ContentBehavior.Append), - new TagHelperDescriptor("prepend", "PrependTagHelper", "SomeAssembly", ContentBehavior.Prepend), - new TagHelperDescriptor("replace", "ReplaceTagHelper", "SomeAssembly", ContentBehavior.Replace) - }; - } - } - public static TheoryData TagHelperDescriptorFlowTestData { get @@ -109,13 +91,6 @@ namespace Microsoft.AspNet.Razor.Test.Generator Enumerable.Empty(), false }, - { - "ContentBehaviorTagHelpers", - "ContentBehaviorTagHelpers", - ContentBehaviorTagHelperDescriptors, - ContentBehaviorTagHelperDescriptors, - false - }, { "ComplexTagHelpers", "ComplexTagHelpers", @@ -144,7 +119,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator bool designTimeMode) { RunTagHelperTest( - testName, + testName, baseLineName: baselineName, tagHelperDescriptors: tagHelperDescriptors, onResults: (results) => @@ -174,7 +149,13 @@ namespace Microsoft.AspNet.Razor.Test.Generator generatedAbsoluteIndex: 475, generatedLineIndex: 15, characterOffsetIndex: 14, - contentLength: 11) + contentLength: 11), + BuildLineMapping(documentAbsoluteIndex: 57, + documentLineIndex: 2, + generatedAbsoluteIndex: 958, + generatedLineIndex: 34, + characterOffsetIndex: 28, + contentLength: 4) } }, { @@ -188,21 +169,13 @@ namespace Microsoft.AspNet.Razor.Test.Generator generatedAbsoluteIndex: 475, generatedLineIndex: 15, characterOffsetIndex: 14, - contentLength: 11) - } - }, - { - "ContentBehaviorTagHelpers", - "ContentBehaviorTagHelpers.DesignTime", - ContentBehaviorTagHelperDescriptors, - new List - { - BuildLineMapping(documentAbsoluteIndex: 14, - documentLineIndex: 0, - generatedAbsoluteIndex: 495, - generatedLineIndex: 15, - characterOffsetIndex: 14, - contentLength: 11) + contentLength: 11), + BuildLineMapping(documentAbsoluteIndex: 189, + documentLineIndex: 6, + generatedAbsoluteIndex: 1574, + generatedLineIndex: 44, + characterOffsetIndex: 40, + contentLength: 4) } }, { @@ -213,17 +186,32 @@ namespace Microsoft.AspNet.Razor.Test.Generator { BuildLineMapping(14, 0, 479, 15, 14, 11), BuildLineMapping(30, 2, 1, 995, 35, 0, 48), - BuildLineMapping(157, 7, 32, 1177, 45, 6, 12), - BuildLineMapping(205, 9, 1260, 50, 0, 12), - BuildLineMapping(218, 9, 13, 1356, 56, 12, 27), - BuildLineMapping(346, 12, 1754, 68, 0, 48), - BuildLineMapping(440, 15, 46, 2004, 78, 6, 8), - 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(205, 9, 1113, 44, 0, 12), + BuildLineMapping(218, 9, 13, 1209, 50, 12, 27), + BuildLineMapping(346, 12, 1607, 62, 0, 48), + BuildLineMapping(440, 15, 46, 1798, 71, 6, 8), + BuildLineMapping(457, 15, 2121, 79, 63, 4), + BuildLineMapping(501, 16, 31, 2328, 86, 6, 30), + BuildLineMapping(568, 17, 30, 2677, 95, 0, 10), + BuildLineMapping(601, 17, 63, 2759, 101, 0, 8), + BuildLineMapping(632, 17, 94, 2839, 107, 0, 1), + BuildLineMapping(639, 18, 3093, 116, 0, 15), + BuildLineMapping(157, 7, 32, 3242, 123, 6, 12), + BuildLineMapping(719, 21, 3325, 128, 0, 12), + BuildLineMapping(733, 21, 3423, 134, 14, 21), + BuildLineMapping(787, 22, 30, 3680, 142, 28, 7), + BuildLineMapping(685, 20, 17, 3836, 148, 19, 23), + BuildLineMapping(708, 20, 40, 3859, 148, 42, 7), + BuildLineMapping(897, 25, 30, 4101, 155, 28, 30), + BuildLineMapping(831, 24, 16, 4280, 161, 19, 8), + BuildLineMapping(840, 24, 25, 4288, 161, 27, 23), + BuildLineMapping(1026, 28, 4546, 168, 28, 30), + BuildLineMapping(964, 27, 16, 4725, 174, 19, 30), + BuildLineMapping(1156, 31, 4990, 181, 28, 3), + BuildLineMapping(1161, 31, 33, 4993, 181, 31, 27), + BuildLineMapping(1189, 31, 61, 5020, 181, 58, 10), + BuildLineMapping(1094, 30, 18, 5179, 187, 19, 29), + BuildLineMapping(1231, 34, 5279, 192, 0, 1), } } }; @@ -256,7 +244,6 @@ namespace Microsoft.AspNet.Razor.Test.Generator { "SingleTagHelper", PAndInputTagHelperDescriptors }, { "BasicTagHelpers", PAndInputTagHelperDescriptors }, { "BasicTagHelpers.RemoveTagHelper", PAndInputTagHelperDescriptors }, - { "ContentBehaviorTagHelpers", ContentBehaviorTagHelperDescriptors }, { "ComplexTagHelpers", PAndInputTagHelperDescriptors }, }; } @@ -312,7 +299,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator RunTagHelperTest("AddTagHelperDirective", designTimeMode: true, tagHelperDescriptors: new[] { - new TagHelperDescriptor("p", "pTagHelper", "SomeAssembly", ContentBehavior.None) + new TagHelperDescriptor("p", "pTagHelper", "SomeAssembly") }); } @@ -328,12 +315,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator new TagHelperDescriptor("MyTagHelper", "MyTagHelper", "SomeAssembly", - ContentBehavior.None, new [] { new TagHelperAttributeDescriptor("BoundProperty", propertyInfo) }), - new TagHelperDescriptor("NestedTagHelper", "NestedTagHelper", "SomeAssembly", ContentBehavior.Modify) + new TagHelperDescriptor("NestedTagHelper", "NestedTagHelper", "SomeAssembly") }; // Act & Assert diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs new file mode 100644 index 0000000000..ea9525b913 --- /dev/null +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpTagHelperRenderingUnitTest.cs @@ -0,0 +1,188 @@ +using System.Collections.Generic; +using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; +using Microsoft.AspNet.Razor.Parser; +using Microsoft.AspNet.Razor.TagHelpers; +using Xunit; + +namespace Microsoft.AspNet.Razor.Test.Generator +{ + public class CSharpTagHelperRenderingUnitTest + { + [Fact] + public void CreatesAUniqueIdForSingleTagHelperChunk() + { + // Arrange + var chunk = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName") + }); + var codeRenderer = CreateCodeRenderer(); + + // Act + codeRenderer.RenderTagHelper(chunk); + + // Assert + Assert.Equal(1, codeRenderer.GenerateUniqueIdCount); + } + + [Fact] + public void UsesTheSameUniqueIdForTagHelperChunkWithMultipleTagHelpers() + { + // Arrange + var chunk = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName"), + new TagHelperDescriptor("div", "Div2TagHelper", "FakeAssemblyName") + }); + var codeRenderer = CreateCodeRenderer(); + + // Act + codeRenderer.RenderTagHelper(chunk); + + // Assert + Assert.Equal(1, codeRenderer.GenerateUniqueIdCount); + } + + [Fact] + public void UsesDifferentUniqueIdForMultipleTagHelperChunksForSameTagHelper() + { + // Arrange + var chunk1 = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName") + }); + var chunk2 = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName") + }); + var codeRenderer = CreateCodeRenderer(); + + // Act + codeRenderer.RenderTagHelper(chunk1); + codeRenderer.RenderTagHelper(chunk2); + + // Assert + Assert.Equal(2, codeRenderer.GenerateUniqueIdCount); + } + + [Fact] + public void UsesDifferentUniqueIdForNestedTagHelperChunksForSameTagHelper() + { + // Arrange + var parentChunk = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName") + }); + var childChunk = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName") + }); + parentChunk.Children.Add(childChunk); + var codeRenderer = CreateCodeRenderer(); + + // Act + codeRenderer.RenderTagHelper(parentChunk); + + // Assert + Assert.Equal(2, codeRenderer.GenerateUniqueIdCount); + } + + [Fact] + public void UsesDifferentUniqueIdForMultipleTagHelperChunksForDifferentTagHelpers() + { + // Arrange + var divChunk = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName") + }); + var spanChunk = CreateTagHelperChunk("span", new[] { + new TagHelperDescriptor("span", "SpanTagHelper", "FakeAssemblyName") + }); + var codeRenderer = CreateCodeRenderer(); + + // Act + codeRenderer.RenderTagHelper(divChunk); + codeRenderer.RenderTagHelper(spanChunk); + + // Assert + Assert.Equal(2, codeRenderer.GenerateUniqueIdCount); + } + + [Fact] + public void UsesCorrectUniqueIdForMultipleTagHelperChunksSomeWithSameSameTagHelpersSomeWithDifferentTagHelpers() + { + // Arrange + var chunk1 = CreateTagHelperChunk("div", new[] { + new TagHelperDescriptor("div", "DivTagHelper", "FakeAssemblyName"), + new TagHelperDescriptor("div", "Div2TagHelper", "FakeAssemblyName") + }); + var chunk2 = CreateTagHelperChunk("span", new[] { + new TagHelperDescriptor("span", "SpanTagHelper", "FakeAssemblyName") + }); + var chunk3 = CreateTagHelperChunk("span", new[] { + new TagHelperDescriptor("span", "SpanTagHelper", "FakeAssemblyName"), + new TagHelperDescriptor("span", "Span2TagHelper", "FakeAssemblyName") + }); + var codeRenderer = CreateCodeRenderer(); + + // Act + codeRenderer.RenderTagHelper(chunk1); + codeRenderer.RenderTagHelper(chunk2); + codeRenderer.RenderTagHelper(chunk3); + + // Assert + Assert.Equal(3, codeRenderer.GenerateUniqueIdCount); + } + + private static TagHelperChunk CreateTagHelperChunk(string tagName, IEnumerable tagHelperDescriptors) + { + return new TagHelperChunk + { + TagName = tagName, + Descriptors = tagHelperDescriptors, + Children = new List(), + Attributes = new Dictionary() + }; + } + + private static TrackingUniqueIdsTagHelperCodeRenderer CreateCodeRenderer() + { + var writer = new CSharpCodeWriter(); + var codeBuilderContext = CreateContext(); + var visitor = new CSharpCodeVisitor(writer, codeBuilderContext); + var codeRenderer = new TrackingUniqueIdsTagHelperCodeRenderer( + visitor, + writer, + codeBuilderContext); + visitor.TagHelperRenderer = codeRenderer; + return codeRenderer; + } + + private static CodeBuilderContext CreateContext() + { + return new CodeBuilderContext( + new CodeGeneratorContext( + new RazorEngineHost(new CSharpRazorCodeLanguage()), + "MyClass", + "MyNamespace", + string.Empty, + shouldGenerateLinePragmas: true), + new ParserErrorSink()); + } + + private class TrackingUniqueIdsTagHelperCodeRenderer : CSharpTagHelperCodeRenderer + { + public TrackingUniqueIdsTagHelperCodeRenderer( + IChunkVisitor bodyVisitor, + CSharpCodeWriter writer, + CodeBuilderContext context) + : base(bodyVisitor, writer, context) + { + + } + + protected override string GenerateUniqueId() + { + GenerateUniqueIdCount++; + return "test"; + } + + public int GenerateUniqueIdCount { get; private set; } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/CSharpCodeBuilderTests.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/CSharpCodeBuilderTests.cs index e5a04834c9..a47d1cb13f 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/CSharpCodeBuilderTests.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/CSharpCodeBuilderTests.cs @@ -3,6 +3,7 @@ #if !ASPNETCORE50 using Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; using Microsoft.AspNet.Razor.Test.Utils; using Moq; @@ -23,7 +24,8 @@ namespace Microsoft.AspNet.Razor.Test.Generator.CodeTree "TestClass", "TestNamespace", "Foo.cs", - shouldGenerateLinePragmas: false); + shouldGenerateLinePragmas: false, + errorSink: new ParserErrorSink()); codeBuilderContext.CodeTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object); codeBuilderContext.CodeTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object); var codeBuilder = language.CreateCodeBuilder(codeBuilderContext); diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/ChunkVisitorTests.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/ChunkVisitorTests.cs index 020fdcb2e0..b7244cb208 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/ChunkVisitorTests.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CodeTree/ChunkVisitorTests.cs @@ -4,6 +4,7 @@ #if !ASPNETCORE50 using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.AspNet.Razor.Parser; using Moq; using Moq.Protected; using Xunit; @@ -34,7 +35,8 @@ namespace Microsoft.AspNet.Razor "myclass", "myns", string.Empty, - shouldGenerateLinePragmas: false); + shouldGenerateLinePragmas: false, + errorSink: new ParserErrorSink()); var writer = Mock.Of(); return new Mock>(writer, codeBuilderContext); } diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs index da41d1fd2d..c5d4ef5b45 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/RazorCodeGeneratorTest.cs @@ -181,17 +181,13 @@ namespace Microsoft.AspNet.Razor.Test.Generator if (expectedDesignTimePragmas != null) { - Assert.True(results.DesignTimeLineMappings != null && results.DesignTimeLineMappings.Count > 0); + Assert.True(results.DesignTimeLineMappings != null); // Guard + for (var i = 0; i < expectedDesignTimePragmas.Count && i < results.DesignTimeLineMappings.Count; i++) + { + Assert.Equal(expectedDesignTimePragmas[i], results.DesignTimeLineMappings[i]); + } Assert.Equal(expectedDesignTimePragmas.Count, results.DesignTimeLineMappings.Count); - - for (var i = 0; i < expectedDesignTimePragmas.Count; i++) - { - if (!expectedDesignTimePragmas[i].Equals(results.DesignTimeLineMappings[i])) - { - Assert.True(false, String.Format("Line mapping {0} is not equivalent.", i)); - } - } } } } diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs index 38471fd741..5269ee8a7a 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperAttributeValueCodeRendererTest.cs @@ -23,18 +23,16 @@ namespace Microsoft.AspNet.Razor.Test.Generator var checkedPropertyInfo = typeof(TestType).GetProperty("Checked"); var tagHelperDescriptors = new TagHelperDescriptor[] { - new TagHelperDescriptor("p", "PTagHelper", "SomeAssembly", ContentBehavior.None), + new TagHelperDescriptor("p", "PTagHelper", "SomeAssembly"), new TagHelperDescriptor("input", "InputTagHelper", "SomeAssembly", - ContentBehavior.None, new TagHelperAttributeDescriptor[] { new TagHelperAttributeDescriptor("type", inputTypePropertyInfo) }), new TagHelperDescriptor("input", "InputTagHelper2", "SomeAssembly", - ContentBehavior.None, new TagHelperAttributeDescriptor[] { new TagHelperAttributeDescriptor("type", inputTypePropertyInfo), new TagHelperAttributeDescriptor("checked", checkedPropertyInfo) @@ -65,7 +63,7 @@ namespace Microsoft.AspNet.Razor.Test.Generator } } - private class AttributeCodeGeneratorReplacingCodeBuilder : CSharpCodeBuilder + private class AttributeCodeGeneratorReplacingCodeBuilder : TestCSharpCodeBuilder { public AttributeCodeGeneratorReplacingCodeBuilder(CodeBuilderContext context) : base(context) @@ -88,11 +86,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/Generator/TagHelperTestBase.cs b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs index 31ac2e2dcd..f6bbfefb16 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/TagHelperTestBase.cs @@ -5,7 +5,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Generator.Compiler; +using Microsoft.AspNet.Razor.Generator.Compiler.CSharp; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.TagHelpers; using Xunit; @@ -78,6 +80,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator _tagHelperDescriptors = tagHelperDescriptors; } + protected internal override CodeBuilder CreateCodeBuilder(CodeBuilderContext context) + { + return Host.DecorateCodeBuilder(new TestCSharpCodeBuilder(context), context); + } + protected internal override RazorParser CreateParser(string fileName) { var parser = base.CreateParser(fileName); @@ -87,5 +94,37 @@ namespace Microsoft.AspNet.Razor.Test.Generator new CustomTagHelperDescriptorResolver(_tagHelperDescriptors)); } } + + protected class TestCSharpCodeBuilder : CSharpCodeBuilder + { + public TestCSharpCodeBuilder(CodeBuilderContext context) + : base(context) + { + + } + + protected override CSharpCodeVisitor CreateCSharpCodeVisitor(CSharpCodeWriter writer, CodeBuilderContext context) + { + var visitor = base.CreateCSharpCodeVisitor(writer, context); + visitor.TagHelperRenderer = new NoUniqueIdsTagHelperCodeRenderer(visitor, writer, context); + return visitor; + } + + private class NoUniqueIdsTagHelperCodeRenderer : CSharpTagHelperCodeRenderer + { + public NoUniqueIdsTagHelperCodeRenderer(IChunkVisitor bodyVisitor, + CSharpCodeWriter writer, + CodeBuilderContext context) + : base(bodyVisitor, writer, context) + { + + } + + protected override string GenerateUniqueId() + { + return "test"; + } + } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs index 5e79d22274..fe3b537915 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpDirectivesTest.cs @@ -247,32 +247,6 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp .AsBaseType("$rootnamespace$.MyBase"))); } - [Fact] - public void SessionStateDirectiveWorks() - { - ParseBlockTest("@sessionstate InProc", - new DirectiveBlock( - Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.SessionStateKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("InProc") - .AsRazorDirectiveAttribute("sessionstate", "InProc") - )); - } - - [Fact] - public void SessionStateDirectiveParsesInvalidSessionValue() - { - ParseBlockTest("@sessionstate Blah", - new DirectiveBlock( - Factory.CodeTransition(), - Factory.MetaCode(SyntaxConstants.CSharp.SessionStateKeyword + " ") - .Accepts(AcceptedCharacters.None), - Factory.Code("Blah") - .AsRazorDirectiveAttribute("sessionstate", "Blah") - )); - } - [Fact] public void FunctionsDirective() { diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs index 604e6d97fe..b80f409894 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/Html/HtmlAttributeTest.cs @@ -190,7 +190,7 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html var rewritten = rewritingContext.SyntaxTree; // Assert - Assert.Equal(0, results.ParserErrors.Count); + Assert.Equal(0, results.ParserErrors.Count()); EvaluateParseTree(rewritten, new MarkupBlock( new MarkupTagBlock( @@ -277,7 +277,7 @@ namespace Microsoft.AspNet.Razor.Test.Parser.Html var rewritten = rewritingContext.SyntaxTree; // Assert - Assert.Equal(0, results.ParserErrors.Count); + Assert.Equal(0, results.ParserErrors.Count()); Assert.Equal(rewritten.Children.Count(), results.Document.Children.Count()); } diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs index 264c8be456..9e6dcc44dd 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/ParserVisitorExtensionsTest.cs @@ -19,9 +19,10 @@ namespace Microsoft.AspNet.Razor.Test.Parser public void VisitThrowsOnNullVisitor() { ParserVisitor target = null; + var errorSink = new ParserErrorSink(); var results = new ParserResults(new BlockBuilder() { Type = BlockType.Comment }.Build(), Enumerable.Empty(), - parserErrors: new List()); + errorSink); Assert.Throws("self", () => target.Visit(results)); } @@ -39,9 +40,10 @@ namespace Microsoft.AspNet.Razor.Test.Parser // Arrange Mock targetMock = new Mock(); var root = new BlockBuilder() { Type = BlockType.Comment }.Build(); + var errorSink = new ParserErrorSink(); var results = new ParserResults(root, Enumerable.Empty(), - parserErrors: new List()); + errorSink); // Act targetMock.Object.Visit(results); @@ -56,11 +58,17 @@ namespace Microsoft.AspNet.Razor.Test.Parser // Arrange Mock targetMock = new Mock(); var root = new BlockBuilder() { Type = BlockType.Comment }.Build(); - List errors = new List() { + var errorSink = new ParserErrorSink(); + List errors = new List + { new RazorError("Foo", 1, 0, 1), - new RazorError("Bar", 2, 0, 2) + new RazorError("Bar", 2, 0, 2), }; - var results = new ParserResults(root, Enumerable.Empty(), errors); + foreach (var error in errors) + { + errorSink.OnError(error); + } + var results = new ParserResults(root, Enumerable.Empty(), errorSink); // Act targetMock.Object.Visit(results); @@ -76,11 +84,10 @@ namespace Microsoft.AspNet.Razor.Test.Parser // Arrange Mock targetMock = new Mock(); var root = new BlockBuilder() { Type = BlockType.Comment }.Build(); - List errors = new List() { - new RazorError("Foo", 1, 0, 1), - new RazorError("Bar", 2, 0, 2) - }; - var results = new ParserResults(root, Enumerable.Empty(), errors); + var errorSink = new ParserErrorSink(); + errorSink.OnError(new RazorError("Foo", 1, 0, 1)); + errorSink.OnError(new RazorError("Bar", 2, 0, 2)); + var results = new ParserResults(root, Enumerable.Empty(), errorSink); // Act targetMock.Object.Visit(results); diff --git a/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs b/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs index 53770f1729..fdc88f124f 100644 --- a/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/RazorTemplateEngineTest.cs @@ -119,7 +119,8 @@ namespace Microsoft.AspNet.Razor.Test "different-class", "different-ns", string.Empty, - shouldGenerateLinePragmas: true); + shouldGenerateLinePragmas: true, + errorSink: new ParserErrorSink()); var expected = new CSharpCodeBuilder(codeBuilderContext); diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs index 6a88364d50..53fa28bd77 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/AddOrRemoveTagHelperSpanVisitorTest.cs @@ -168,8 +168,10 @@ namespace Microsoft.AspNet.Razor.TagHelpers new ParserErrorSink()); var document = new MarkupBlock(Factory.Markup("Hello World")); - // Act & Assert - Assert.DoesNotThrow(() => addOrRemoveTagHelperSpanVisitor.GetDescriptors(document)); + // Act + var descriptors = addOrRemoveTagHelperSpanVisitor.GetDescriptors(document); + + Assert.Empty(descriptors); } private class TestTagHelperDescriptorResolver : ITagHelperDescriptorResolver diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorProviderTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorProviderTest.cs index 62a05a9608..017ae4a9ed 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorProviderTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperDescriptorProviderTest.cs @@ -13,8 +13,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers public void TagHelperDescriptorProvider_GetTagHelpersReturnsNothingForUnregisteredTags() { // Arrange - var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly", ContentBehavior.None); - var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly", ContentBehavior.None); + var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); + var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly"); var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); @@ -29,9 +29,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers public void TagHelperDescriptorProvider_GetTagHelpersDoesNotReturnNonCatchAllTagsForCatchAll() { // Arrange - var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly", ContentBehavior.None); - var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly", ContentBehavior.None); - var catchAllDescriptor = new TagHelperDescriptor("*", "foo3", "SomeAssembly", ContentBehavior.None); + var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); + var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly"); + var catchAllDescriptor = new TagHelperDescriptor("*", "foo3", "SomeAssembly"); var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); @@ -47,9 +47,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers public void TagHelperDescriptorProvider_GetTagHelpersReturnsCatchAllsWithEveryTagName() { // Arrange - var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly", ContentBehavior.None); - var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly", ContentBehavior.None); - var catchAllDescriptor = new TagHelperDescriptor("*", "foo3", "SomeAssembly", ContentBehavior.None); + var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); + var spanDescriptor = new TagHelperDescriptor("span", "foo2", "SomeAssembly"); + var catchAllDescriptor = new TagHelperDescriptor("*", "foo3", "SomeAssembly"); var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); @@ -73,7 +73,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers public void TagHelperDescriptorProvider_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool() { // Arrange - var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly", ContentBehavior.None); + var divDescriptor = new TagHelperDescriptor("div", "foo1", "SomeAssembly"); var descriptors = new TagHelperDescriptor[] { divDescriptor, divDescriptor }; var provider = new TagHelperDescriptorProvider(descriptors); diff --git a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs index b78cbd1430..2425da44e1 100644 --- a/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/TagHelpers/TagHelperParseTreeRewriterTest.cs @@ -19,6 +19,430 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { public class TagHelperParseTreeRewriterTest : CsHtmlMarkupParserTestBase { + public static TheoryData MalformedTagHelperAttributeBlockData + { + get + { + var factory = CreateDefaultSpanFactory(); + var blockFactory = new BlockFactory(factory); + var errorFormatUnclosed = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + + "end tag or be self closing."; + var errorFormatNoCloseAngle = "Missing close angle for tag helper '{0}'."; + var errorFormatNoCSharp = "The tag helper '{0}' must not have C# in the element's attribute " + + "declaration area."; + Func createInvalidDoBlock = extraCode => + { + return new MarkupBlock( + new MarkupBlock( + new StatementBlock( + factory.CodeTransition(), + factory.Code("do {" + extraCode).AsStatement()))); + }; + var dateTimeNow = new MarkupBlock( + new MarkupBlock( + new ExpressionBlock( + factory.CodeTransition(), + factory.Code("DateTime.Now") + .AsImplicitExpression(CSharpCodeParser.DefaultKeywords) + .Accepts(AcceptedCharacters.NonWhiteSpace)))); + + return new TheoryData + { + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new Dictionary())), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

", + new MarkupBlock( + new MarkupTagHelperBlock("p", + new Dictionary + { + { "bar", factory.Markup("false") } + })), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + SourceLocation.Zero) + } + }, + { + "

+ { + { + "", + new MarkupBlock( + new MarkupTagHelperBlock("strong", + new MarkupTagHelperBlock("p"))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatNoCloseAngle, "strong"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + SourceLocation.Zero), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + absoluteIndex: 8, lineIndex: 0, columnIndex: 8) + } + }, + { + " <

", + new MarkupBlock( + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock(""), + factory.Markup(" "), + blockFactory.MarkupTagBlock("<"), + new MarkupTagHelperBlock("p")), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + absoluteIndex: 2, lineIndex: 0, columnIndex: 2), + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + absoluteIndex: 13, lineIndex: 0, columnIndex: 13) + } + }, + { + "<<> <<>>", + new MarkupBlock( + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<"), + new MarkupTagHelperBlock("strong", + factory.Markup("> "), + blockFactory.MarkupTagBlock("<"), + blockFactory.MarkupTagBlock("<>"), + factory.Markup(">"))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "strong"), + absoluteIndex: 2, lineIndex: 0, columnIndex: 2) + } + }, + { + "

", + new MarkupBlock( + blockFactory.MarkupTagBlock(""))), + new [] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, errorFormatUnclosed, "p"), + absoluteIndex: 12, lineIndex: 0, columnIndex: 12) + } + } + }; + } + } + + [Theory] + [MemberData(nameof(MalformedTagHelperBlockData))] + public void Rewrite_CreatesErrorForMalformedTagHelper( + string documentContent, + MarkupBlock expectedOutput, + RazorError[] expectedErrors) + { + RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); + } + public static TheoryData CodeTagHelperAttributesData { get @@ -94,7 +518,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers // Arrange var descriptors = new TagHelperDescriptor[] { - new TagHelperDescriptor("person", "PersonTagHelper", "personAssembly", ContentBehavior.None, + new TagHelperDescriptor("person", "PersonTagHelper", "personAssembly", attributes: new[] { new TagHelperAttributeDescriptor("age", "Age", typeof(int).FullName), @@ -105,9 +529,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers var providerContext = new TagHelperDescriptorProvider(descriptors); // Act & Assert - EvaluateData(providerContext, - documentContent, - expectedOutput, + EvaluateData(providerContext, + documentContent, + expectedOutput, expectedErrors: Enumerable.Empty()); } @@ -117,8 +541,8 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { var factory = CreateDefaultSpanFactory(); var blockFactory = new BlockFactory(factory); - var errorFormat = "Found a malformed '{0}' tag helper. Tag helpers must have a start and end tag or " + - "be self closing."; + var malformedErrorFormat = "Found a malformed '{0}' tag helper. Tag helpers must have a start and " + + "end tag or be self closing."; var dateTimeNow = new MarkupBlock( new ExpressionBlock( factory.CodeTransition(), @@ -130,16 +554,23 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers "

", new MarkupBlock( new MarkupTagHelperBlock("p", - new Dictionary - { - { "class", factory.Markup("foo") }, - { "dynamic", new MarkupBlock(dateTimeNow) }, - { "style", factory.Markup("color:red;") } - }, - new MarkupTagHelperBlock("strong", - blockFactory.MarkupTagBlock("

")))), - new RazorError(string.Format(CultureInfo.InvariantCulture, errorFormat, "p"), - SourceLocation.Zero) + new Dictionary + { + { "class", factory.Markup("foo") }, + { "dynamic", new MarkupBlock(dateTimeNow) }, + { "style", factory.Markup("color:red;") } + }, + new MarkupTagHelperBlock("strong")), + blockFactory.MarkupTagBlock("")), + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), + absoluteIndex: 52, lineIndex: 0, columnIndex: 52), + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), + absoluteIndex: 64, lineIndex: 0, columnIndex: 64) + } }; yield return new object[] { "

Hello World

", @@ -150,8 +581,12 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers new MarkupTagHelperBlock("strong", factory.Markup("World")), blockFactory.MarkupTagBlock("
"))), - new RazorError(string.Format(CultureInfo.InvariantCulture, errorFormat, "p"), - absoluteIndex: 5, lineIndex: 0, columnIndex: 5) + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), + absoluteIndex: 5, lineIndex: 0, columnIndex: 5) + } }; yield return new object[] { "

Hello World

", @@ -162,8 +597,15 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers new MarkupTagHelperBlock("strong", factory.Markup("World"), blockFactory.MarkupTagBlock("
")))), - new RazorError(string.Format(CultureInfo.InvariantCulture, errorFormat, "strong"), - absoluteIndex: 14, lineIndex: 0, columnIndex: 14) + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), + absoluteIndex: 5, lineIndex: 0, columnIndex: 5), + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "strong"), + absoluteIndex: 14, lineIndex: 0, columnIndex: 14) + } }; yield return new object[] { "

Hello

World

", @@ -180,8 +622,12 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers { "style", factory.Markup("color:red;") } }, factory.Markup("World")))), - new RazorError(string.Format(CultureInfo.InvariantCulture, errorFormat, "p"), - SourceLocation.Zero) + new RazorError[] + { + new RazorError( + string.Format(CultureInfo.InvariantCulture, malformedErrorFormat, "p"), + SourceLocation.Zero) + } }; } } @@ -191,9 +637,9 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers public void TagHelperParseTreeRewriter_CreatesErrorForIncompleteTagHelper( string documentContent, MarkupBlock expectedOutput, - RazorError expectedError) + RazorError[] expectedErrors) { - RunParseTreeRewriterTest(documentContent, expectedOutput, new[] { expectedError }, "strong", "p"); + RunParseTreeRewriterTest(documentContent, expectedOutput, expectedErrors, "strong", "p"); } public static TheoryData InvalidHtmlBlockData @@ -1268,7 +1714,7 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers foreach (var tagName in tagNames) { descriptors.Add( - new TagHelperDescriptor(tagName, tagName + "taghelper", "SomeAssembly", ContentBehavior.None)); + new TagHelperDescriptor(tagName, tagName + "taghelper", "SomeAssembly")); } return new TagHelperDescriptorProvider(descriptors); @@ -1292,8 +1738,10 @@ namespace Microsoft.AspNet.Razor.Test.TagHelpers var rewritingContext = new RewritingContext(results.Document, errorSink); new TagHelperParseTreeRewriter(provider).Rewrite(rewritingContext); var rewritten = rewritingContext.SyntaxTree; + var actualErrors = errorSink.Errors.OrderBy(error => error.Location.AbsoluteIndex) + .ToList(); - EvaluateRazorErrors(errorSink.Errors.ToList(), expectedErrors.ToList()); + EvaluateRazorErrors(actualErrors, expectedErrors.ToList()); EvaluateParseTree(rewritten, expectedOutput); } 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 0db29ec3af..d1561120b7 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 @@ -7,7 +7,9 @@ namespace TestOutput public class BasicTagHelpers { #line hidden + #pragma warning disable 0414 private System.IO.TextWriter __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 private TagHelperExecutionContext __tagHelperExecutionContext = null; private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); @@ -25,57 +27,117 @@ namespace TestOutput Instrumentation.BeginContext(27, 49, true); WriteLiteral("\r\n
\r\n "); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __PTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__PTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.Type = **From custom attribute code renderer**: "text"; + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __InputTagHelper2 = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper2); + __InputTagHelper2.Type = __InputTagHelper.Type; + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.Type = **From custom attribute code renderer**: "checkbox"; + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); __PTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__PTagHelper); __tagHelperExecutionContext.AddHtmlAttribute("class", "Hello World"); __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(99, 10, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); - __PTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__PTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(116, 10, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - __InputTagHelper.Type = **From custom attribute code renderer**: "text"; - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(147, 10, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - __InputTagHelper.Type = **From custom attribute code renderer**: "checkbox"; - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __InputTagHelper2.Checked = **From custom attribute code renderer**: true; - __tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(196, 6, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); Instrumentation.BeginContext(206, 8, true); 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..911e0f75f1 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 @@ -32,7 +32,6 @@ namespace TestOutput #pragma warning disable 1998 public override async Task ExecuteAsync() { - __PTagHelper = CreateTagHelper(); __PTagHelper = CreateTagHelper(); __InputTagHelper = CreateTagHelper(); __InputTagHelper.Type = "text"; @@ -42,7 +41,12 @@ namespace TestOutput __InputTagHelper.Type = "checkbox"; __InputTagHelper2 = CreateTagHelper(); __InputTagHelper2.Type = __InputTagHelper.Type; +#line 7 "BasicTagHelpers.cshtml" __InputTagHelper2.Checked = true; + +#line default +#line hidden + __PTagHelper = CreateTagHelper(); } #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 174c0d754a..5b14a8f598 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 @@ -8,7 +8,9 @@ namespace TestOutput public class BasicTagHelpers { #line hidden + #pragma warning disable 0414 private System.IO.TextWriter __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 private TagHelperExecutionContext __tagHelperExecutionContext = null; private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); @@ -26,57 +28,117 @@ namespace TestOutput Instrumentation.BeginContext(27, 49, true); WriteLiteral("\r\n
\r\n "); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __PTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__PTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.Type = "text"; + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __InputTagHelper2 = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper2); + __InputTagHelper2.Type = __InputTagHelper.Type; + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.Type = "checkbox"; + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); __PTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__PTagHelper); __tagHelperExecutionContext.AddHtmlAttribute("class", "Hello World"); __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(99, 10, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); - __PTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__PTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(116, 10, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - __InputTagHelper.Type = "text"; - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(147, 10, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - __InputTagHelper.Type = "checkbox"; - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __InputTagHelper2.Checked = true; - __tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(196, 6, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); Instrumentation.BeginContext(206, 8, true); 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..b63a5d2e8b 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 @@ -41,12 +41,6 @@ if (true) #line default #line hidden - __PTagHelper = CreateTagHelper(); -#line 8 "ComplexTagHelpers.cshtml" -__o = DateTime.Now; - -#line default -#line hidden #line 10 "ComplexTagHelpers.cshtml" @@ -60,11 +54,11 @@ __o = DateTime.Now; #line default #line hidden - __PTagHelper = CreateTagHelper(); __InputTagHelper = CreateTagHelper(); __InputTagHelper.Type = "text"; __InputTagHelper2 = CreateTagHelper(); __InputTagHelper2.Type = __InputTagHelper.Type; + __PTagHelper = CreateTagHelper(); #line 13 "ComplexTagHelpers.cshtml" } else @@ -73,7 +67,6 @@ __o = DateTime.Now; #line default #line hidden - __PTagHelper = CreateTagHelper(); __InputTagHelper = CreateTagHelper(); #line 16 "ComplexTagHelpers.cshtml" __o = checkbox; @@ -83,7 +76,12 @@ __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 + __PTagHelper = CreateTagHelper(); __InputTagHelper = CreateTagHelper(); #line 17 "ComplexTagHelpers.cshtml" __o = true ? "checkbox" : "anything"; @@ -121,7 +119,77 @@ if(true) { #line default #line hidden + __PTagHelper = CreateTagHelper(); +#line 8 "ComplexTagHelpers.cshtml" +__o = DateTime.Now; + +#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 21 "ComplexTagHelpers.cshtml" +__PTagHelper.Age = DateTimeOffset.Now.Year - 1970; + +#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 25 "ComplexTagHelpers.cshtml" +__PTagHelper.Age = -1970 + DateTimeOffset.Now.Year; + +#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 28 "ComplexTagHelpers.cshtml" +__PTagHelper.Age = DateTimeOffset.Now.Year - 1970; + +#line default +#line hidden + __InputTagHelper = CreateTagHelper(); + __InputTagHelper2 = CreateTagHelper(); +#line 32 "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 +#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 09148a9f90..1d2ce6feb2 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; @@ -8,7 +8,9 @@ namespace TestOutput public class ComplexTagHelpers { #line hidden + #pragma warning disable 0414 private System.IO.TextWriter __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 private TagHelperExecutionContext __tagHelperExecutionContext = null; private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); @@ -38,23 +40,8 @@ namespace TestOutput Instrumentation.BeginContext(78, 55, true); WriteLiteral("
\r\n "); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); - __PTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__PTagHelper); - StartWritingScope(); - WriteLiteral("Current Time: "); -#line 8 "ComplexTagHelpers.cshtml" -Write(DateTime.Now); - -#line default -#line hidden - __tagHelperStringValueBuffer = EndWritingScope(); - __tagHelperExecutionContext.AddHtmlAttribute("time", __tagHelperStringValueBuffer.ToString()); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(171, 34, true); - WriteLiteral("\r\n

Set Time:

\r\n"); - Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n

Set Time:

\r\n"); #line 10 "ComplexTagHelpers.cshtml" @@ -68,36 +55,62 @@ Write(DateTime.Now); #line default #line hidden - Instrumentation.BeginContext(245, 16, true); - WriteLiteral(" "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); - __PTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__PTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(264, 10, true); - WriteLiteral("New Time: "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - __InputTagHelper.Type = "text"; - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __tagHelperExecutionContext.AddHtmlAttribute("value", ""); - __tagHelperExecutionContext.AddHtmlAttribute("placeholder", "Enter in a new time..."); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(344, 2, true); - WriteLiteral("\r\n"); - Instrumentation.EndContext(); + WriteLiteral(" "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("New Time: "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + __InputTagHelper.Type = "text"; + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __InputTagHelper2 = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper2); + __InputTagHelper2.Type = __InputTagHelper.Type; + __tagHelperExecutionContext.AddHtmlAttribute("value", ""); + __tagHelperExecutionContext.AddHtmlAttribute("placeholder", "Enter in a new time..."); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + } + , StartWritingScope, EndWritingScope); + __PTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__PTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n"); #line 13 "ComplexTagHelpers.cshtml" } else @@ -106,117 +119,475 @@ Write(DateTime.Now); #line default #line hidden - Instrumentation.BeginContext(394, 16, true); - WriteLiteral(" "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); - __PTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__PTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(413, 14, true); - WriteLiteral("Current Time: "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - StartWritingScope(); + WriteLiteral(" "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("Current Time: "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + StartWritingScope(); #line 16 "ComplexTagHelpers.cshtml" Write(checkbox); #line default #line hidden - __tagHelperStringValueBuffer = EndWritingScope(); - __InputTagHelper.Type = __tagHelperStringValueBuffer.ToString(); - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __InputTagHelper2.Checked = true; - __tagHelperExecutionContext.AddTagHelperAttribute("checked", __InputTagHelper2.Checked); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(468, 18, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - StartWritingScope(); + __tagHelperStringValueBuffer = EndWritingScope(); + __InputTagHelper.Type = __tagHelperStringValueBuffer.ToString(); + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __InputTagHelper2 = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper2); + __InputTagHelper2.Type = __InputTagHelper.Type; +#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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + } + , StartWritingScope, EndWritingScope); + __PTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__PTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + StartWritingScope(); #line 17 "ComplexTagHelpers.cshtml" Write(true ? "checkbox" : "anything"); #line default #line hidden - __tagHelperStringValueBuffer = EndWritingScope(); - __InputTagHelper.Type = __tagHelperStringValueBuffer.ToString(); - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(536, 18, true); - WriteLiteral("\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input"); - __InputTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper); - StartWritingScope(); + __tagHelperStringValueBuffer = EndWritingScope(); + __InputTagHelper.Type = __tagHelperStringValueBuffer.ToString(); + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __InputTagHelper2 = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper2); + __InputTagHelper2.Type = __InputTagHelper.Type; + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __InputTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper); + StartWritingScope(); #line 18 "ComplexTagHelpers.cshtml" if(true) { #line default #line hidden - WriteLiteral(" checkbox "); + WriteLiteral(" checkbox "); #line 18 "ComplexTagHelpers.cshtml" } else { #line default #line hidden - WriteLiteral(" anything "); + WriteLiteral(" anything "); #line 18 "ComplexTagHelpers.cshtml" } #line default #line hidden - __tagHelperStringValueBuffer = EndWritingScope(); - __InputTagHelper.Type = __tagHelperStringValueBuffer.ToString(); - __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); - __InputTagHelper2 = CreateTagHelper(); - __tagHelperExecutionContext.Add(__InputTagHelper2); - __InputTagHelper2.Type = __InputTagHelper.Type; - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(637, 2, true); - WriteLiteral("\r\n"); - Instrumentation.EndContext(); + __tagHelperStringValueBuffer = EndWritingScope(); + __InputTagHelper.Type = __tagHelperStringValueBuffer.ToString(); + __tagHelperExecutionContext.AddTagHelperAttribute("type", __InputTagHelper.Type); + __InputTagHelper2 = CreateTagHelper(); + __tagHelperExecutionContext.Add(__InputTagHelper2); + __InputTagHelper2.Type = __InputTagHelper.Type; + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n"); #line 19 "ComplexTagHelpers.cshtml" } #line default #line hidden - Instrumentation.BeginContext(654, 8, true); - WriteLiteral(" "); - Instrumentation.EndContext(); + WriteLiteral(" "); + } + , StartWritingScope, EndWritingScope); + __PTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__PTagHelper); + StartWritingScope(); + WriteLiteral("Current Time: "); +#line 8 "ComplexTagHelpers.cshtml" +Write(DateTime.Now); + +#line default +#line hidden + __tagHelperStringValueBuffer = EndWritingScope(); + __tagHelperExecutionContext.AddHtmlAttribute("time", __tagHelperStringValueBuffer.ToString()); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(666, 14, true); + Instrumentation.BeginContext(666, 10, true); + WriteLiteral("\r\n "); + Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n"); +#line 22 "ComplexTagHelpers.cshtml" + + +#line default +#line hidden + +#line 22 "ComplexTagHelpers.cshtml" + var @object = false; + +#line default +#line hidden + + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __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.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); + __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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + Instrumentation.BeginContext(813, 10, true); + WriteLiteral("\r\n "); + Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __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.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); + __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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + Instrumentation.BeginContext(946, 10, true); + WriteLiteral("\r\n "); + Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __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.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); + __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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + Instrumentation.BeginContext(1074, 10, true); + WriteLiteral("\r\n "); + Instrumentation.EndContext(); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", "test", async() => { + } + , StartWritingScope, EndWritingScope); + __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.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); + __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()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + Instrumentation.BeginContext(1217, 14, true); WriteLiteral("\r\n
\r\n"); Instrumentation.EndContext(); -#line 22 "ComplexTagHelpers.cshtml" +#line 35 "ComplexTagHelpers.cshtml" } #line default diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ContentBehaviorTagHelpers.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ContentBehaviorTagHelpers.DesignTime.cs deleted file mode 100644 index 58140f8f3c..0000000000 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ContentBehaviorTagHelpers.DesignTime.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace TestOutput -{ - using Microsoft.AspNet.Razor.Runtime.TagHelpers; - using System; - using System.Threading.Tasks; - - public class ContentBehaviorTagHelpers - { - private static object @__o; - private void @__RazorDesignTimeHelpers__() - { - #pragma warning disable 219 - string __tagHelperDirectiveSyntaxHelper = null; - __tagHelperDirectiveSyntaxHelper = -#line 1 "ContentBehaviorTagHelpers.cshtml" - "something" - -#line default -#line hidden - ; - #pragma warning restore 219 - } - #line hidden - private ModifyTagHelper __ModifyTagHelper = null; - private NoneTagHelper __NoneTagHelper = null; - private AppendTagHelper __AppendTagHelper = null; - private PrependTagHelper __PrependTagHelper = null; - private ReplaceTagHelper __ReplaceTagHelper = null; - #line hidden - public ContentBehaviorTagHelpers() - { - } - - #pragma warning disable 1998 - public override async Task ExecuteAsync() - { - __ModifyTagHelper = CreateTagHelper(); - __NoneTagHelper = CreateTagHelper(); - __AppendTagHelper = CreateTagHelper(); - __PrependTagHelper = CreateTagHelper(); - __ReplaceTagHelper = CreateTagHelper(); - } - #pragma warning restore 1998 - } -} diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ContentBehaviorTagHelpers.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ContentBehaviorTagHelpers.cs deleted file mode 100644 index c9e8f1adc3..0000000000 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/ContentBehaviorTagHelpers.cs +++ /dev/null @@ -1,83 +0,0 @@ -#pragma checksum "ContentBehaviorTagHelpers.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "5aeeb0a2b5283b353efaff2cd2fb0c739020b899" -namespace TestOutput -{ - using Microsoft.AspNet.Razor.Runtime.TagHelpers; - using System; - using System.Threading.Tasks; - - public class ContentBehaviorTagHelpers - { - #line hidden - private System.IO.TextWriter __tagHelperStringValueBuffer = null; - private TagHelperExecutionContext __tagHelperExecutionContext = null; - private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); - private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); - private ModifyTagHelper __ModifyTagHelper = null; - private NoneTagHelper __NoneTagHelper = null; - private AppendTagHelper __AppendTagHelper = null; - private PrependTagHelper __PrependTagHelper = null; - private ReplaceTagHelper __ReplaceTagHelper = null; - #line hidden - public ContentBehaviorTagHelpers() - { - } - - #pragma warning disable 1998 - public override async Task ExecuteAsync() - { - Instrumentation.BeginContext(27, 2, true); - WriteLiteral("\r\n"); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("modify"); - __ModifyTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__ModifyTagHelper); - StartWritingScope(); - WriteLiteral("\r\n "); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("none"); - __NoneTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__NoneTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral("\r\n "); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("append"); - __AppendTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__AppendTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral("\r\n "); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("prepend"); - __PrependTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__PrependTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); - WriteLiteral("\r\n "); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("replace"); - __ReplaceTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__ReplaceTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - WriteLiteral("\r\n "); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - WriteLiteral("\r\n "); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - WriteLiteral("\r\n "); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - WriteLiteral("\r\n"); - __tagHelperStringValueBuffer = EndWritingScope(); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext, __tagHelperStringValueBuffer).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); - WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - } - #pragma warning restore 1998 - } -} 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 5d5aa2a289..4fb7796615 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 @@ -8,7 +8,9 @@ namespace TestOutput public class SingleTagHelper { #line hidden + #pragma warning disable 0414 private System.IO.TextWriter __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 private TagHelperExecutionContext __tagHelperExecutionContext = null; private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); @@ -24,17 +26,35 @@ namespace TestOutput Instrumentation.BeginContext(27, 2, true); WriteLiteral("\r\n"); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p"); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("p", "test", async() => { + WriteLiteral("Body of Tag"); + } + , StartWritingScope, EndWritingScope); __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; WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(63, 11, true); - WriteLiteral("Body of Tag"); - Instrumentation.EndContext(); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInHelper.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInHelper.cs index c24bfdee97..9259ead1c9 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInHelper.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInHelper.cs @@ -24,7 +24,40 @@ MyHelper(string val) Instrumentation.BeginContext(62, 19, true); WriteLiteralTo(__razor_helper_writer, "
\r\n "); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("mytaghelper"); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("mytaghelper", "test", async() => { + WriteLiteral("\r\n In None ContentBehavior.\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("nestedtaghelper", "test", async() => { + WriteLiteral("Some buffered values with a value of "); +#line 8 "TagHelpersInHelper.cshtml" + Write(val); + +#line default +#line hidden + } + , StartWritingScope, EndWritingScope); + __NestedTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__NestedTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); __MyTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__MyTagHelper); StartWritingScope(); @@ -48,28 +81,22 @@ Write(DateTime.Now); __tagHelperExecutionContext.AddHtmlAttribute("unboundproperty", __tagHelperStringValueBuffer.ToString()); __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(184, 52, true); - WriteLiteralTo(__razor_helper_writer, "\r\n In None ContentBehavior.\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("nestedtaghelper"); - __NestedTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__NestedTagHelper); - StartWritingScope(); - WriteLiteral("Some buffered values with a value of "); -#line 8 "TagHelpersInHelper.cshtml" - Write(val); - -#line default -#line hidden - __tagHelperStringValueBuffer = EndWritingScope(); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext, __tagHelperStringValueBuffer).Result; - WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GenerateContent()); - WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(312, 10, true); - WriteLiteralTo(__razor_helper_writer, "\r\n "); - Instrumentation.EndContext(); + WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + StartWritingScope(__razor_helper_writer); + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + EndWritingScope(); + } + WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteralTo(__razor_helper_writer, __tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); Instrumentation.BeginContext(336, 14, true); @@ -89,7 +116,9 @@ Write(DateTime.Now); #line hidden #line hidden + #pragma warning disable 0414 private System.IO.TextWriter __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 private TagHelperExecutionContext __tagHelperExecutionContext = null; private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); @@ -106,23 +135,33 @@ Write(DateTime.Now); Instrumentation.BeginContext(27, 2, true); WriteLiteral("\r\n"); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("mytaghelper"); - __MyTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__MyTagHelper); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; - WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(367, 9, false); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("mytaghelper", "test", async() => { #line 12 "TagHelpersInHelper.cshtml" Write(MyHelper(item => new Template((__razor_template_writer) => { - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("nestedtaghelper"); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("nestedtaghelper", "test", async() => { + WriteLiteral("Custom Value"); + } + , StartWritingScope, EndWritingScope); __NestedTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__NestedTagHelper); - StartWritingScope(); - WriteLiteral("Custom Value"); - __tagHelperStringValueBuffer = EndWritingScope(); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext, __tagHelperStringValueBuffer).Result; + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateContent()); + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + StartWritingScope(__razor_template_writer); + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + EndWritingScope(); + } + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); } @@ -131,7 +170,26 @@ Write(MyHelper(item => new Template((__razor_template_writer) => { #line default #line hidden - Instrumentation.EndContext(); + } + , StartWritingScope, EndWritingScope); + __MyTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__MyTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); Instrumentation.BeginContext(439, 2, true); diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInSection.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInSection.cs index 0f53e8a4d5..5763367a13 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInSection.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/TagHelpersInSection.cs @@ -7,7 +7,9 @@ namespace TestOutput public class TagHelpersInSection { #line hidden + #pragma warning disable 0414 private System.IO.TextWriter __tagHelperStringValueBuffer = null; + #pragma warning restore 0414 private TagHelperExecutionContext __tagHelperExecutionContext = null; private TagHelperRunner __tagHelperRunner = new TagHelperRunner(); private TagHelperScopeManager __tagHelperScopeManager = new TagHelperScopeManager(); @@ -38,7 +40,40 @@ namespace TestOutput Instrumentation.BeginContext(87, 21, true); WriteLiteralTo(__razor_template_writer, "\r\n
\r\n "); Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("mytaghelper"); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("mytaghelper", "test", async() => { + WriteLiteral("\r\n In None ContentBehavior.\r\n "); + __tagHelperExecutionContext = __tagHelperScopeManager.Begin("nestedtaghelper", "test", async() => { + WriteLiteral("Some buffered values with "); +#line 11 "TagHelpersInSection.cshtml" + Write(code); + +#line default +#line hidden + } + , StartWritingScope, EndWritingScope); + __NestedTagHelper = CreateTagHelper(); + __tagHelperExecutionContext.Add(__NestedTagHelper); + __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; + WriteLiteral(__tagHelperExecutionContext.Output.GenerateStartTag()); + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteral(__tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteral(__tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + } + WriteLiteral(__tagHelperExecutionContext.Output.GeneratePostContent()); + WriteLiteral(__tagHelperExecutionContext.Output.GenerateEndTag()); + __tagHelperExecutionContext = __tagHelperScopeManager.End(); + WriteLiteral("\r\n "); + } + , StartWritingScope, EndWritingScope); __MyTagHelper = CreateTagHelper(); __tagHelperExecutionContext.Add(__MyTagHelper); StartWritingScope(); @@ -62,28 +97,22 @@ Write(DateTime.Now); __tagHelperExecutionContext.AddHtmlAttribute("unboundproperty", __tagHelperStringValueBuffer.ToString()); __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext).Result; WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateStartTag()); - Instrumentation.BeginContext(211, 52, true); - WriteLiteralTo(__razor_template_writer, "\r\n In None ContentBehavior.\r\n "); - Instrumentation.EndContext(); - __tagHelperExecutionContext = __tagHelperScopeManager.Begin("nestedtaghelper"); - __NestedTagHelper = CreateTagHelper(); - __tagHelperExecutionContext.Add(__NestedTagHelper); - StartWritingScope(); - WriteLiteral("Some buffered values with "); -#line 11 "TagHelpersInSection.cshtml" - Write(code); - -#line default -#line hidden - __tagHelperStringValueBuffer = EndWritingScope(); - __tagHelperExecutionContext.Output = __tagHelperRunner.RunAsync(__tagHelperExecutionContext, __tagHelperStringValueBuffer).Result; - WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateStartTag()); - WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateContent()); - WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateEndTag()); - __tagHelperExecutionContext = __tagHelperScopeManager.End(); - Instrumentation.BeginContext(329, 10, true); - WriteLiteralTo(__razor_template_writer, "\r\n "); - Instrumentation.EndContext(); + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GeneratePreContent()); + if (__tagHelperExecutionContext.Output.ContentSet) + { + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateContent()); + } + else if (__tagHelperExecutionContext.ChildContentRetrieved) + { + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.GetChildContentAsync().Result); + } + else + { + StartWritingScope(__razor_template_writer); + __tagHelperExecutionContext.ExecuteChildContentAsync().Wait(); + EndWritingScope(); + } + WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GeneratePostContent()); WriteLiteralTo(__razor_template_writer, __tagHelperExecutionContext.Output.GenerateEndTag()); __tagHelperExecutionContext = __tagHelperScopeManager.End(); Instrumentation.BeginContext(353, 14, true); 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 diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ContentBehaviorTagHelpers.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ContentBehaviorTagHelpers.cshtml deleted file mode 100644 index 90f4fcd204..0000000000 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/ContentBehaviorTagHelpers.cshtml +++ /dev/null @@ -1,11 +0,0 @@ -@addtaghelper "something" - - - - - - This should not be in the output - - - - \ No newline at end of file diff --git a/test/Microsoft.AspNet.Razor.Test/project.json b/test/Microsoft.AspNet.Razor.Test/project.json index 77b9804fa1..cc96a8b09d 100644 --- a/test/Microsoft.AspNet.Razor.Test/project.json +++ b/test/Microsoft.AspNet.Razor.Test/project.json @@ -5,17 +5,21 @@ "Moq": "4.2.1312.1622", "Microsoft.AspNet.Razor": "4.0.0-*", "Microsoft.AspNet.Testing": "1.0.0-*", - "Xunit.KRunner": "1.0.0-*" + "xunit.runner.kre": "1.0.0-*" }, "commands": { - "test": "Xunit.KRunner" + "test": "xunit.runner.kre" }, "frameworks": { "aspnet50": { }, "aspnetcore50": { "dependencies": { - "System.Diagnostics.TraceSource": "4.0.0-beta-*" + "System.Diagnostics.TraceSource": "4.0.0-beta-*", + "System.Reflection.TypeExtensions": "4.0.0-beta-*" } } + }, + "compilationOptions": { + "define": [ "__RemoveThisBitTo__GENERATE_BASELINES" ] } }