From b67b8dae3d2cd8ebcce7f420baddc5c38ea516eb Mon Sep 17 00:00:00 2001 From: "N. Taylor Mullen" Date: Mon, 29 Sep 2014 17:35:17 -0700 Subject: [PATCH] Add @addtaghelper directive. - Also added some infrastructure pieces for it such as the ITagHelperDescriptorResolver and the default implementation TagHelperDescriptorResolver which will be filled out in a later commit. - Reworked some extensibility points to allow accessibility of the descriptor resolvers per offline discussions. #111 --- .../CodeBuilder/CSharp/CSharpCodeBuilder.cs | 2 +- .../CSharp/Visitors/CSharpCodeVisitor.cs | 20 +++++- .../CSharpDesignTimeHelpersVisitor.cs | 42 ++++++++++- .../Compiler/CodeBuilder/ChunkVisitor.cs | 5 ++ .../Compiler/CodeBuilder/CodeVisitor.cs | 3 + .../Chunks/TagHelpers/AddTagHelperChunk.cs | 16 +++++ .../Compiler/CodeTree/CodeTreeBuilder.cs | 8 +++ .../TagHelpers/AddTagHelperCodeGenerator.cs | 42 +++++++++++ .../Parser/CSharpCodeParser.Directives.cs | 69 +++++++++++++++++++ .../Parser/CSharpCodeParser.cs | 1 + .../Parser/RazorParser.cs | 33 +++++---- .../Parser/SyntaxConstants.cs | 1 + .../TagHelperRegistrationVisitor.cs | 60 ++++++++++++++++ .../Properties/RazorResources.Designer.cs | 48 +++++++++++++ src/Microsoft.AspNet.Razor/RazorEngineHost.cs | 7 ++ .../RazorResources.resx | 9 +++ .../RazorTemplateEngine.cs | 8 ++- .../ITagHelperDescriptorResolver.cs | 14 ++++ .../TagHelpers/TagHelperDescriptorResolver.cs | 18 +++++ 19 files changed, 381 insertions(+), 25 deletions(-) create mode 100644 src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/AddTagHelperChunk.cs create mode 100644 src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs create mode 100644 src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs create mode 100644 src/Microsoft.AspNet.Razor/TagHelpers/ITagHelperDescriptorResolver.cs create mode 100644 src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolver.cs 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 3cb2ee5ccd..4556ac2f12 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/CSharpCodeBuilder.cs @@ -56,7 +56,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp new CSharpHelperVisitor(csharpCodeVisitor, writer, Context).Accept(Tree.Chunks); new CSharpTypeMemberVisitor(csharpCodeVisitor, writer, Context).Accept(Tree.Chunks); - new CSharpDesignTimeHelpersVisitor(writer, Context).AcceptTree(Tree); + new CSharpDesignTimeHelpersVisitor(csharpCodeVisitor, writer, Context).AcceptTree(Tree); new CSharpTagHelperFieldDeclarationVisitor(writer, Context).Accept(Tree.Chunks); BuildConstructor(writer); diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs index 3162bc5e26..8a11542cbc 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpCodeVisitor.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp private const string TemplateWriterName = "__razor_template_writer"; private CSharpPaddingBuilder _paddingBuilder; + private CSharpTagHelperCodeRenderer _tagHelperCodeRenderer; public CSharpCodeVisitor(CSharpCodeWriter writer, CodeBuilderContext context) : base(writer, context) @@ -25,7 +26,22 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp TagHelperRenderer = new CSharpTagHelperCodeRenderer(this, writer, context); } - public CSharpTagHelperCodeRenderer TagHelperRenderer { get; set; } + public CSharpTagHelperCodeRenderer TagHelperRenderer + { + get + { + return _tagHelperCodeRenderer; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(TagHelperRenderer)); + } + + _tagHelperCodeRenderer = value; + } + } protected override void Visit(TagHelperChunk chunk) { @@ -485,7 +501,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp return Context.Host.EnableInstrumentation && Context.ExpressionRenderingMode == ExpressionRenderingMode.WriteToOutput; } - + private CSharpCodeWriter RenderPreWriteStart() { return RenderPreWriteStart(Writer, Context); diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs index 358aa28a83..ce56958acd 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CSharp/Visitors/CSharpDesignTimeHelpersVisitor.cs @@ -1,6 +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.Diagnostics; +using System.Globalization; + namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp { public class CSharpDesignTimeHelpersVisitor : CodeVisitor @@ -8,10 +11,21 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp internal const string InheritsHelper = "__inheritsHelper"; internal const string DesignTimeHelperMethodName = "__RazorDesignTimeHelpers__"; + private const string TagHelperDirectiveSyntaxHelper = "__tagHelperDirectiveSyntaxHelper"; private const int DisableVariableNamingWarnings = 219; - public CSharpDesignTimeHelpersVisitor(CSharpCodeWriter writer, CodeBuilderContext context) - : base(writer, context) { } + private readonly CSharpCodeVisitor _csharpCodeVisitor; + + private bool _initializedTagHelperDirectiveSyntaxHelper; + + public CSharpDesignTimeHelpersVisitor([NotNull] CSharpCodeVisitor csharpCodeVisitor, + [NotNull] CSharpCodeWriter writer, + [NotNull] CodeBuilderContext context) + + : base(writer, context) + { + _csharpCodeVisitor = csharpCodeVisitor; + } public void AcceptTree(CodeTree tree) { @@ -43,5 +57,29 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler.CSharp } } } + + protected override void Visit(AddTagHelperChunk chunk) + { + // We should always be in design time mode because of the calling AcceptTree method verification. + Debug.Assert(Context.Host.DesignTimeMode); + + if (!_initializedTagHelperDirectiveSyntaxHelper) + { + _initializedTagHelperDirectiveSyntaxHelper = true; + Writer.WriteVariableDeclaration("string", TagHelperDirectiveSyntaxHelper, "null"); + } + + Writer.WriteStartAssignment(TagHelperDirectiveSyntaxHelper); + + // The parsing mechanism for the AddTagHelperChunk (CSharpCodeParser.TagHelperDirective()) removes quotes + // that surround the chunk.LookupText. + _csharpCodeVisitor.CreateExpressionCodeMapping( + string.Format( + CultureInfo.InvariantCulture, + "\"{0}\"", chunk.LookupText), + chunk); + + Writer.WriteLine(";"); + } } } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs index 4d2a9389f1..22db1736ae 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/ChunkVisitor.cs @@ -57,6 +57,10 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler { Visit((TagHelperChunk)chunk); } + else if (chunk is AddTagHelperChunk) + { + Visit((AddTagHelperChunk)chunk); + } else if(chunk is SetLayoutChunk) { Visit((SetLayoutChunk)chunk); @@ -115,6 +119,7 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler protected abstract void Visit(ExpressionChunk chunk); protected abstract void Visit(StatementChunk chunk); protected abstract void Visit(TagHelperChunk chunk); + protected abstract void Visit(AddTagHelperChunk chunk); protected abstract void Visit(UsingChunk chunk); protected abstract void Visit(ChunkBlock chunk); protected abstract void Visit(DynamicCodeAttributeChunk chunk); diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs index 5e964489cd..618524f252 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeBuilder/CodeVisitor.cs @@ -32,6 +32,9 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler protected override void Visit(TagHelperChunk chunk) { } + protected override void Visit(AddTagHelperChunk chunk) + { + } protected override void Visit(LiteralCodeAttributeChunk chunk) { } diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/AddTagHelperChunk.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/AddTagHelperChunk.cs new file mode 100644 index 0000000000..35df594bf4 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/Chunks/TagHelpers/AddTagHelperChunk.cs @@ -0,0 +1,16 @@ +// 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 +{ + /// + /// A used to look up s. + /// + public class AddTagHelperChunk : Chunk + { + /// + /// Text used to look up s. + /// + public string LookupText { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs index 1dcb51a214..0a0d920930 100644 --- a/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs +++ b/src/Microsoft.AspNet.Razor/Generator/Compiler/CodeTree/CodeTreeBuilder.cs @@ -39,6 +39,14 @@ namespace Microsoft.AspNet.Razor.Generator.Compiler } } + public void AddAddTagHelperChunk(string lookupText, SyntaxTreeNode association) + { + AddChunk(new AddTagHelperChunk + { + LookupText = lookupText + }, association); + } + public void AddLiteralChunk(string literal, SyntaxTreeNode association) { // If the previous chunk was also a LiteralChunk, append the content of the current node to the previous one. diff --git a/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs new file mode 100644 index 0000000000..9f79630974 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Generator/TagHelpers/AddTagHelperCodeGenerator.cs @@ -0,0 +1,42 @@ +// 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.SyntaxTree; + +namespace Microsoft.AspNet.Razor.Generator +{ + /// + /// A responsible for generating s. + /// + public class AddTagHelperCodeGenerator : SpanCodeGenerator + { + /// + /// Instantiates a new . + /// + /// + /// Text used to look up s. + /// + public AddTagHelperCodeGenerator(string lookupText) + { + LookupText = lookupText; + } + + /// + /// Text used to look up s. + /// + public string LookupText { get; private set; } + + /// + /// Generates a . + /// + /// + /// The responsible for this . + /// + /// A instance that contains information about + /// the current code generation process. + public override void GenerateCode(Span target, CodeGeneratorContext context) + { + context.CodeTreeBuilder.AddAddTagHelperChunk(LookupText, target); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs index 5fba093cce..03b6c4ae6d 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Directives.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNet.Razor.Parser { private void SetupDirectives() { + MapDirectives(AddTagHelperDirective, SyntaxConstants.CSharp.AddTagHelperKeyword); MapDirectives(InheritsDirective, SyntaxConstants.CSharp.InheritsKeyword); MapDirectives(FunctionsDirective, SyntaxConstants.CSharp.FunctionsKeyword); MapDirectives(SectionDirective, SyntaxConstants.CSharp.SectionKeyword); @@ -27,6 +28,14 @@ namespace Microsoft.AspNet.Razor.Parser MapDirectives(SessionStateDirective, SyntaxConstants.CSharp.SessionStateKeyword); } + protected virtual void AddTagHelperDirective() + { + TagHelperDirective(SyntaxConstants.CSharp.AddTagHelperKeyword, (lookupText) => + { + return new AddTagHelperCodeGenerator(lookupText); + }); + } + protected virtual void LayoutDirective() { AssertDirective(SyntaxConstants.CSharp.LayoutKeyword); @@ -498,5 +507,65 @@ namespace Microsoft.AspNet.Razor.Parser CompleteBlock(); Output(SpanKind.Code); } + + private void TagHelperDirective(string keyword, Func codeGeneratorBuilder) + { + AssertDirective(keyword); + + // Accept the directive name + AcceptAndMoveNext(); + + // Set the block type + Context.CurrentBlock.Type = BlockType.Directive; + + var foundWhitespace = At(CSharpSymbolType.WhiteSpace); + AcceptWhile(CSharpSymbolType.WhiteSpace); + + // If we found whitespace then any content placed within the whitespace MAY cause a destructive change + // to the document. We can't accept it. + Output(SpanKind.MetaCode, foundWhitespace ? AcceptedCharacters.None : AcceptedCharacters.Any); + + if (EndOfFile || At(CSharpSymbolType.NewLine)) + { + Context.OnError(CurrentLocation, RazorResources.FormatParseError_DirectiveMustHaveValue(keyword)); + } + else + { + // Need to grab the current location before we accept until the end of the line. + var startLocation = CurrentLocation; + + // Parse to the end of the line. Essentially accepts anything until end of line, comments, invalid code + // etc. + AcceptUntil(CSharpSymbolType.NewLine); + + // Pull out the value minus the spaces at the end + var rawValue = Span.GetContent().Value.TrimEnd(); + var startsWithQuote = rawValue.StartsWith("\"", StringComparison.OrdinalIgnoreCase); + + // If the value starts with a quote then we should generate appropriate C# code to colorize the value. + if (startsWithQuote) + { + // Set up code generation + // The generated chunk of this code generator is picked up by CSharpDesignTimeHelpersVisitor which + // renders the C# to colorize the user provided value. We trim the quotes around the user's value + // so when we render the code we can project the users value into double quotes to not invoke C# + // IntelliSense. + Span.CodeGenerator = codeGeneratorBuilder(rawValue.Trim('"')); + } + + // We expect the directive to be surrounded in quotes. + // The format for taghelper directives are: @directivename "SomeValue" + if (!startsWithQuote || + !rawValue.EndsWith("\"", StringComparison.OrdinalIgnoreCase)) + { + Context.OnError(startLocation, + RazorResources.FormatParseError_DirectiveMustBeSurroundedByQuotes(keyword)); + } + } + + // Output the span and finish the block + CompleteBlock(); + Output(SpanKind.Code); + } } } diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs index 41f3a74489..c26241b6a9 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.cs @@ -19,6 +19,7 @@ namespace Microsoft.AspNet.Razor.Parser internal static ISet DefaultKeywords = new HashSet() { + SyntaxConstants.CSharp.AddTagHelperKeyword, "if", "do", "try", diff --git a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs index 8d1a42d583..1bf6b83698 100644 --- a/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs +++ b/src/Microsoft.AspNet.Razor/Parser/RazorParser.cs @@ -17,25 +17,16 @@ namespace Microsoft.AspNet.Razor.Parser { public class RazorParser { - public RazorParser(ParserBase codeParser, ParserBase markupParser) - { - if (codeParser == null) - { - throw new ArgumentNullException("codeParser"); - } - if (markupParser == null) - { - throw new ArgumentNullException("markupParser"); - } + private ITagHelperDescriptorResolver _tagHelperDescriptorResolver; + public RazorParser([NotNull] ParserBase codeParser, + [NotNull] ParserBase markupParser, + ITagHelperDescriptorResolver tagHelperDescriptorResolver) + { + _tagHelperDescriptorResolver = tagHelperDescriptorResolver; MarkupParser = markupParser; CodeParser = codeParser; - // TODO: As part of https://github.com/aspnet/Razor/issues/111 and - // https://github.com/aspnet/Razor/issues/112 pull the provider from some sort of tag helper locator - // object. - var provider = new TagHelperDescriptorProvider(Enumerable.Empty()); - Optimizers = new List() { // TODO: Modify the below WhiteSpaceRewriter & ConditionalAttributeCollapser to handle @@ -45,8 +36,6 @@ namespace Microsoft.AspNet.Razor.Parser new WhiteSpaceRewriter(MarkupParser.BuildSpan), // Collapse conditional attributes where the entire value is literal new ConditionalAttributeCollapser(MarkupParser.BuildSpan), - // Enables tag helpers - new TagHelperParseTreeRewriter(provider), }; } @@ -153,6 +142,16 @@ namespace Microsoft.AspNet.Razor.Parser current = rewriter.Rewrite(current); } + if (_tagHelperDescriptorResolver != null) + { + var tagHelperRegistrationVisitor = new TagHelperRegistrationVisitor(_tagHelperDescriptorResolver); + var tagHelperProvider = tagHelperRegistrationVisitor.CreateProvider(current); + + var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider); + // Rewrite the document to utilize tag helpers + current = tagHelperParseTreeRewriter.Rewrite(current); + } + // Link the leaf nodes into a chain Span prev = null; foreach (Span node in current.Flatten()) diff --git a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs index 3f879e7042..e931fd0f5f 100644 --- a/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs +++ b/src/Microsoft.AspNet.Razor/Parser/SyntaxConstants.cs @@ -18,6 +18,7 @@ namespace Microsoft.AspNet.Razor.Parser public static class CSharp { public static readonly int UsingKeywordLength = 5; + public static readonly string AddTagHelperKeyword = "addtaghelper"; public static readonly string InheritsKeyword = "inherits"; public static readonly string FunctionsKeyword = "functions"; public static readonly string SectionKeyword = "section"; diff --git a/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs new file mode 100644 index 0000000000..b59342e39d --- /dev/null +++ b/src/Microsoft.AspNet.Razor/Parser/TagHelpers/TagHelperRegistrationVisitor.cs @@ -0,0 +1,60 @@ +// 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 Microsoft.AspNet.Razor.Generator; +using Microsoft.AspNet.Razor.Parser.SyntaxTree; +using Microsoft.AspNet.Razor.TagHelpers; + +namespace Microsoft.AspNet.Razor.Parser.TagHelpers.Internal +{ + public class TagHelperRegistrationVisitor : ParserVisitor + { + private readonly ITagHelperDescriptorResolver _descriptorResolver; + + private HashSet _descriptors; + + public TagHelperRegistrationVisitor(ITagHelperDescriptorResolver descriptorResolver) + { + _descriptorResolver = descriptorResolver; + } + + public TagHelperDescriptorProvider CreateProvider(Block root) + { + _descriptors = new HashSet(TagHelperDescriptorComparer.Default); + + // This will recurse through the syntax tree. + VisitBlock(root); + + return new TagHelperDescriptorProvider(_descriptors); + } + + public override void VisitSpan(Span span) + { + // We're only interested in spans with an AddTagHelperCodeGenerator. + if (span.CodeGenerator is AddTagHelperCodeGenerator) + { + if (_descriptorResolver == null) + { + throw new InvalidOperationException( + RazorResources.FormatTagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver( + SyntaxConstants.CSharp.AddTagHelperKeyword, + nameof(TagHelperDescriptorResolver), + nameof(RazorParser))); + } + + var addGenerator = (AddTagHelperCodeGenerator)span.CodeGenerator; + + // Look up all the descriptors associated with the "LookupText". + var descriptors = _descriptorResolver.Resolve(addGenerator.LookupText); + + // Add all the found descriptors to our HashSet. + foreach (var descriptor in descriptors) + { + _descriptors.Add(descriptor); + } + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs index 34467e11d5..4d965eec63 100644 --- a/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs +++ b/src/Microsoft.AspNet.Razor/Properties/RazorResources.Designer.cs @@ -1494,6 +1494,54 @@ namespace Microsoft.AspNet.Razor return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_AttributesThatAreNotStringsMustNotContainAtSymbols"), p0); } + /// + /// Directive '{0}' must have a value. + /// + internal static string ParseError_DirectiveMustHaveValue + { + get { return GetString("ParseError_DirectiveMustHaveValue"); } + } + + /// + /// Directive '{0}' must have a value. + /// + internal static string FormatParseError_DirectiveMustHaveValue(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_DirectiveMustHaveValue"), p0); + } + + /// + /// Directive '{0}'s value must be surrounded in double quotes. + /// + internal static string ParseError_DirectiveMustBeSurroundedByQuotes + { + get { return GetString("ParseError_DirectiveMustBeSurroundedByQuotes"); } + } + + /// + /// Directive '{0}'s value must be surrounded in double quotes. + /// + internal static string FormatParseError_DirectiveMustBeSurroundedByQuotes(object p0) + { + return string.Format(CultureInfo.CurrentCulture, GetString("ParseError_DirectiveMustBeSurroundedByQuotes"), p0); + } + + /// + /// Cannot use directive '{0}' when a {1} has not been provided to the {2}. + /// + internal static string TagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver + { + get { return GetString("TagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver"); } + } + + /// + /// Cannot use directive '{0}' when a {1} has not been provided to the {2}. + /// + internal static string FormatTagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver(object p0, object p1, object p2) + { + return string.Format(CultureInfo.CurrentCulture, GetString("TagHelpers_CannotUseDirectiveWithNoTagHelperDescriptorResolver"), p0, p1, p2); + } + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNet.Razor/RazorEngineHost.cs b/src/Microsoft.AspNet.Razor/RazorEngineHost.cs index 39d4edcdb2..ca38205e8c 100644 --- a/src/Microsoft.AspNet.Razor/RazorEngineHost.cs +++ b/src/Microsoft.AspNet.Razor/RazorEngineHost.cs @@ -8,6 +8,7 @@ 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; namespace Microsoft.AspNet.Razor { @@ -160,6 +161,12 @@ namespace Microsoft.AspNet.Razor return null; } + // TODO: Document this as part of https://github.com/aspnet/Razor/issues/99 + public virtual ITagHelperDescriptorResolver CreateTagHelperDescriptorResolver() + { + return new TagHelperDescriptorResolver(); + } + /// /// Gets an instance of the code parser and is provided an opportunity to decorate or replace it /// diff --git a/src/Microsoft.AspNet.Razor/RazorResources.resx b/src/Microsoft.AspNet.Razor/RazorResources.resx index 6fafd8e7b9..b258cdc9c3 100644 --- a/src/Microsoft.AspNet.Razor/RazorResources.resx +++ b/src/Microsoft.AspNet.Razor/RazorResources.resx @@ -415,4 +415,13 @@ Instead, wrap the contents of the block in "{{}}": 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. + + + Directive '{0}'s value must be surrounded in double quotes. + + + Cannot use directive '{0}' when a {1} has not been provided to the {2}. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs b/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs index 20e1654e88..5bb72bba22 100644 --- a/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs +++ b/src/Microsoft.AspNet.Razor/RazorTemplateEngine.cs @@ -269,11 +269,13 @@ namespace Microsoft.AspNet.Razor protected internal virtual RazorParser CreateParser() { - ParserBase codeParser = Host.CodeLanguage.CreateCodeParser(); - ParserBase markupParser = Host.CreateMarkupParser(); + var codeParser = Host.CodeLanguage.CreateCodeParser(); + var markupParser = Host.CreateMarkupParser(); + var tagHelperDescriptorResolver = Host.CreateTagHelperDescriptorResolver(); return new RazorParser(Host.DecorateCodeParser(codeParser), - Host.DecorateMarkupParser(markupParser)) + Host.DecorateMarkupParser(markupParser), + tagHelperDescriptorResolver) { DesignTimeMode = Host.DesignTimeMode }; diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/ITagHelperDescriptorResolver.cs b/src/Microsoft.AspNet.Razor/TagHelpers/ITagHelperDescriptorResolver.cs new file mode 100644 index 0000000000..a479278f09 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/TagHelpers/ITagHelperDescriptorResolver.cs @@ -0,0 +1,14 @@ +// 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.Collections.Generic; + +namespace Microsoft.AspNet.Razor.TagHelpers +{ + // TODO: Document this class as part of https://github.com/aspnet/Razor/issues/99 + + public interface ITagHelperDescriptorResolver + { + IEnumerable Resolve(string lookupText); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolver.cs b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolver.cs new file mode 100644 index 0000000000..9b8f5901c7 --- /dev/null +++ b/src/Microsoft.AspNet.Razor/TagHelpers/TagHelperDescriptorResolver.cs @@ -0,0 +1,18 @@ +// 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.Collections.Generic; +using System.Linq; + +namespace Microsoft.AspNet.Razor.TagHelpers +{ + // TODO: Implement this class as part of https://github.com/aspnet/Razor/issues/99 + + public class TagHelperDescriptorResolver : ITagHelperDescriptorResolver + { + public IEnumerable Resolve(string lookupText) + { + return Enumerable.Empty(); + } + } +} \ No newline at end of file