// 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
{
///
/// Initializes a new instance of .
///
/// The used for parsing code content.
/// The used for parsing markup content.
/// The used to resolve
/// s.
public RazorParser([NotNull] ParserBase codeParser,
[NotNull] ParserBase markupParser,
ITagHelperDescriptorResolver tagHelperDescriptorResolver)
: this(codeParser,
markupParser,
tagHelperDescriptorResolver,
GetDefaultRewriters(markupParser))
{
}
///
/// Initializes a new instance of from the specified .
///
/// The to copy values from.
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 optimizers)
{
CodeParser = codeParser;
MarkupParser = markupParser;
TagHelperDescriptorResolver = tagHelperDescriptorResolver;
Optimizers = optimizers.ToList();
}
public bool DesignTimeMode { get; set; }
///
/// Gets the used to resolve s.
///
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 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 spanCallback, Action errorCallback)
{
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback));
}
public virtual Task CreateParseTask(TextReader input, Action spanCallback, Action errorCallback, SynchronizationContext context)
{
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback) { SynchronizationContext = context });
}
public virtual Task CreateParseTask(TextReader input, Action spanCallback, Action errorCallback, CancellationToken cancelToken)
{
return CreateParseTask(input, new CallbackVisitor(spanCallback, errorCallback) { CancelToken = cancelToken });
}
public virtual Task CreateParseTask(TextReader input, Action spanCallback, Action 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();
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);
}
///
/// Returns a sequence of s for tag helpers that are registered in the
/// specified .
///
/// The to scan for tag helper registrations in.
/// Used to manage s encountered during the Razor parsing
/// phase.
/// s that are applicable to the
///
protected virtual IEnumerable GetTagHelperDescriptors([NotNull] Block documentRoot,
[NotNull] ErrorSink errorSink)
{
var addOrRemoveTagHelperSpanVisitor =
new TagHelperDirectiveSpanVisitor(TagHelperDescriptorResolver, errorSink);
return addOrRemoveTagHelperSpanVisitor.GetDescriptors(documentRoot);
}
private static IEnumerable 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),
};
}
}
}