236 lines
9.7 KiB
C#
236 lines
9.7 KiB
C#
// 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.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNet.Razor.Parser.SyntaxTree;
|
|
using Microsoft.AspNet.Razor.Parser.TagHelpers;
|
|
using Microsoft.AspNet.Razor.Parser.TagHelpers.Internal;
|
|
using Microsoft.AspNet.Razor.TagHelpers;
|
|
using Microsoft.AspNet.Razor.Text;
|
|
using Microsoft.Framework.Internal;
|
|
|
|
namespace Microsoft.AspNet.Razor.Parser
|
|
{
|
|
public class RazorParser
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of <see cref="RazorParser"/>.
|
|
/// </summary>
|
|
/// <param name="codeParser">The <see cref="ParserBase"/> used for parsing code content.</param>
|
|
/// <param name="markupParser">The <see cref="ParserBase"/> used for parsing markup content.</param>
|
|
/// <param name="tagHelperDescriptorResolver">The <see cref="ITagHelperDescriptorResolver"/> used to resolve
|
|
/// <see cref="TagHelperDescriptor"/>s.</param>
|
|
public RazorParser([NotNull] ParserBase codeParser,
|
|
[NotNull] ParserBase markupParser,
|
|
ITagHelperDescriptorResolver tagHelperDescriptorResolver)
|
|
: this(codeParser,
|
|
markupParser,
|
|
tagHelperDescriptorResolver,
|
|
GetDefaultRewriters(markupParser))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of <see cref="RazorParser"/> from the specified <paramref name="parser" />.
|
|
/// </summary>
|
|
/// <param name="parser">The <see cref="RazorParser"/> to copy values from.</param>
|
|
public RazorParser([NotNull] RazorParser parser)
|
|
: this(parser.CodeParser, parser.MarkupParser, parser.TagHelperDescriptorResolver, parser.Optimizers)
|
|
{
|
|
DesignTimeMode = parser.DesignTimeMode;
|
|
}
|
|
|
|
private RazorParser(ParserBase codeParser,
|
|
ParserBase markupParser,
|
|
ITagHelperDescriptorResolver tagHelperDescriptorResolver,
|
|
IEnumerable<ISyntaxTreeRewriter> optimizers)
|
|
{
|
|
CodeParser = codeParser;
|
|
MarkupParser = markupParser;
|
|
TagHelperDescriptorResolver = tagHelperDescriptorResolver;
|
|
Optimizers = optimizers.ToList();
|
|
}
|
|
|
|
public bool DesignTimeMode { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets the <see cref="ITagHelperDescriptorResolver"/> used to resolve <see cref="TagHelperDescriptor"/>s.
|
|
/// </summary>
|
|
protected ITagHelperDescriptorResolver TagHelperDescriptorResolver { get; private set; }
|
|
|
|
// Internal for unit testing
|
|
internal ParserBase CodeParser { get; private set; }
|
|
|
|
// Internal for unit testing
|
|
internal ParserBase MarkupParser { get; private set; }
|
|
|
|
// Internal for unit testing
|
|
internal List<ISyntaxTreeRewriter> Optimizers { get; private set; }
|
|
|
|
public virtual void Parse(TextReader input, ParserVisitor visitor)
|
|
{
|
|
Parse(new SeekableTextReader(input), visitor);
|
|
}
|
|
|
|
public virtual ParserResults Parse(TextReader input)
|
|
{
|
|
return ParseCore(new SeekableTextReader(input));
|
|
}
|
|
|
|
public virtual ParserResults Parse(ITextDocument input)
|
|
{
|
|
return ParseCore(input);
|
|
}
|
|
|
|
#pragma warning disable 0618
|
|
[Obsolete("Lookahead-based readers have been deprecated, use overrides which accept a TextReader or ITextDocument instead")]
|
|
public virtual void Parse(LookaheadTextReader input, ParserVisitor visitor)
|
|
{
|
|
var results = ParseCore(new SeekableTextReader(input));
|
|
|
|
// Replay the results on the visitor
|
|
visitor.Visit(results);
|
|
}
|
|
|
|
[Obsolete("Lookahead-based readers have been deprecated, use overrides which accept a TextReader or ITextDocument instead")]
|
|
public virtual ParserResults Parse(LookaheadTextReader input)
|
|
{
|
|
return ParseCore(new SeekableTextReader(input));
|
|
}
|
|
#pragma warning restore 0618
|
|
|
|
public virtual Task CreateParseTask(TextReader input, Action<Span> spanCallback, Action<RazorError> errorCallback)
|
|
{
|
|
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback));
|
|
}
|
|
|
|
public virtual Task CreateParseTask(TextReader input, Action<Span> spanCallback, Action<RazorError> errorCallback, SynchronizationContext context)
|
|
{
|
|
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback) { SynchronizationContext = context });
|
|
}
|
|
|
|
public virtual Task CreateParseTask(TextReader input, Action<Span> spanCallback, Action<RazorError> errorCallback, CancellationToken cancelToken)
|
|
{
|
|
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback) { CancelToken = cancelToken });
|
|
}
|
|
|
|
public virtual Task CreateParseTask(TextReader input, Action<Span> spanCallback, Action<RazorError> errorCallback, SynchronizationContext context, CancellationToken cancelToken)
|
|
{
|
|
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback)
|
|
{
|
|
SynchronizationContext = context,
|
|
CancelToken = cancelToken
|
|
});
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Web.FxCop", "MW1200:DoNotConstructTaskInstances", Justification = "This rule is not applicable to this assembly.")]
|
|
public virtual Task CreateParseTask(TextReader input,
|
|
ParserVisitor consumer)
|
|
{
|
|
return new Task(() =>
|
|
{
|
|
try
|
|
{
|
|
Parse(input, consumer);
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
return; // Just return if we're cancelled.
|
|
}
|
|
});
|
|
}
|
|
|
|
private ParserResults ParseCore(ITextDocument input)
|
|
{
|
|
// Setup the parser context
|
|
var errorSink = new ErrorSink();
|
|
var context = new ParserContext(input, CodeParser, MarkupParser, MarkupParser, errorSink)
|
|
{
|
|
DesignTimeMode = DesignTimeMode
|
|
};
|
|
|
|
MarkupParser.Context = context;
|
|
CodeParser.Context = context;
|
|
|
|
// Execute the parse
|
|
MarkupParser.ParseDocument();
|
|
|
|
// Get the result
|
|
var results = context.CompleteParse();
|
|
|
|
// Rewrite whitespace if supported
|
|
var rewritingContext = new RewritingContext(results.Document, errorSink);
|
|
foreach (ISyntaxTreeRewriter rewriter in Optimizers)
|
|
{
|
|
rewriter.Rewrite(rewritingContext);
|
|
}
|
|
|
|
var descriptors = Enumerable.Empty<TagHelperDescriptor>();
|
|
|
|
if (TagHelperDescriptorResolver != null)
|
|
{
|
|
descriptors = GetTagHelperDescriptors(rewritingContext.SyntaxTree, rewritingContext.ErrorSink);
|
|
var tagHelperProvider = new TagHelperDescriptorProvider(descriptors);
|
|
|
|
var tagHelperParseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperProvider);
|
|
// Rewrite the document to utilize tag helpers
|
|
tagHelperParseTreeRewriter.Rewrite(rewritingContext);
|
|
}
|
|
|
|
var syntaxTree = rewritingContext.SyntaxTree;
|
|
|
|
// Link the leaf nodes into a chain
|
|
Span prev = null;
|
|
foreach (Span node in syntaxTree.Flatten())
|
|
{
|
|
node.Previous = prev;
|
|
if (prev != null)
|
|
{
|
|
prev.Next = node;
|
|
}
|
|
prev = node;
|
|
}
|
|
|
|
// Return the new result
|
|
return new ParserResults(syntaxTree, descriptors, errorSink);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a sequence of <see cref="TagHelperDescriptor"/>s for tag helpers that are registered in the
|
|
/// specified <paramref name="documentRoot"/>.
|
|
/// </summary>
|
|
/// <param name="documentRoot">The <see cref="Block"/> to scan for tag helper registrations in.</param>
|
|
/// <param name="errorSink">Used to manage <see cref="RazorError"/>s encountered during the Razor parsing
|
|
/// phase.</param>
|
|
/// <returns><see cref="TagHelperDescriptor"/>s that are applicable to the <paramref name="documentRoot"/>
|
|
/// </returns>
|
|
protected virtual IEnumerable<TagHelperDescriptor> GetTagHelperDescriptors([NotNull] Block documentRoot,
|
|
[NotNull] ErrorSink errorSink)
|
|
{
|
|
var addOrRemoveTagHelperSpanVisitor =
|
|
new TagHelperDirectiveSpanVisitor(TagHelperDescriptorResolver, errorSink);
|
|
return addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot);
|
|
}
|
|
|
|
private static IEnumerable<ISyntaxTreeRewriter> GetDefaultRewriters(ParserBase markupParser)
|
|
{
|
|
return new ISyntaxTreeRewriter[]
|
|
{
|
|
// TODO: Modify the below WhiteSpaceRewriter & ConditionalAttributeCollapser to handle
|
|
// TagHelperBlock's: https://github.com/aspnet/Razor/issues/117
|
|
|
|
// Move whitespace from start of expression block to markup
|
|
new WhiteSpaceRewriter(markupParser.BuildSpan),
|
|
// Collapse conditional attributes where the entire value is literal
|
|
new ConditionalAttributeCollapser(markupParser.BuildSpan),
|
|
};
|
|
}
|
|
}
|
|
}
|