Add parsing of includes and imports

Also adds the source document to the RazorSyntaxTree and does some cleanup
related to this. This lets us verify which tree goes to which document and
that seems important.

Added basic tests to verify that parsing happens, though it's not being
used for anything right now.
This commit is contained in:
Ryan Nowak 2017-01-13 10:43:59 -08:00
parent 5fa4672c99
commit 242ea0ee3f
14 changed files with 203 additions and 33 deletions

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
if (errorSink.Errors.Count > 0) if (errorSink.Errors.Count > 0)
{ {
var combinedErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors).ToList(); var combinedErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors).ToList();
syntaxTree = RazorSyntaxTree.Create(syntaxTree.Root, combinedErrors, syntaxTree.Options); syntaxTree = RazorSyntaxTree.Create(syntaxTree.Root, syntaxTree.Source, combinedErrors, syntaxTree.Options);
} }
return syntaxTree; return syntaxTree;

View File

@ -24,6 +24,20 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var syntaxTree = RazorSyntaxTree.Parse(codeDocument.Source, options); var syntaxTree = RazorSyntaxTree.Parse(codeDocument.Source, options);
codeDocument.SetSyntaxTree(syntaxTree); codeDocument.SetSyntaxTree(syntaxTree);
var importSyntaxTrees = new RazorSyntaxTree[codeDocument.Imports.Count];
for (var i = 0; i < codeDocument.Imports.Count; i++)
{
importSyntaxTrees[i] = RazorSyntaxTree.Parse(codeDocument.Imports[i], options);
}
codeDocument.SetImportSyntaxTrees(importSyntaxTrees);
var includeSyntaxTrees = new RazorSyntaxTree[codeDocument.Includes.Count];
for (var i = 0; i < codeDocument.Includes.Count; i++)
{
includeSyntaxTrees[i] = RazorSyntaxTree.Parse(codeDocument.Includes[i], options);
}
codeDocument.SetIncludeSyntaxTrees(includeSyntaxTrees);
} }
} }
} }

View File

@ -8,9 +8,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{ {
internal class DefaultRazorSyntaxTree : RazorSyntaxTree internal class DefaultRazorSyntaxTree : RazorSyntaxTree
{ {
public DefaultRazorSyntaxTree(Block root, IReadOnlyList<RazorError> diagnostics, RazorParserOptions options) public DefaultRazorSyntaxTree(
Block root,
RazorSourceDocument source,
IReadOnlyList<RazorError> diagnostics,
RazorParserOptions options)
{ {
Root = root; Root = root;
Source = source;
Diagnostics = diagnostics; Diagnostics = diagnostics;
Options = options; Options = options;
} }
@ -20,5 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public override RazorParserOptions Options { get; } public override RazorParserOptions Options { get; }
internal override Block Root { get; } internal override Block Root { get; }
public override RazorSourceDocument Source { get; }
} }
} }

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var whitespaceRewriter = new WhiteSpaceRewriter(); var whitespaceRewriter = new WhiteSpaceRewriter();
rewritten = whitespaceRewriter.Rewrite(rewritten); rewritten = whitespaceRewriter.Rewrite(rewritten);
var rewrittenSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Diagnostics, syntaxTree.Options); var rewrittenSyntaxTree = RazorSyntaxTree.Create(rewritten, syntaxTree.Source, syntaxTree.Diagnostics, syntaxTree.Options);
return rewrittenSyntaxTree; return rewrittenSyntaxTree;
} }
} }

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.IO;
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{ {
@ -25,17 +24,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public RazorParserOptions Options { get; } public RazorParserOptions Options { get; }
public virtual RazorSyntaxTree Parse(TextReader input) => Parse(input.ReadToEnd()); public virtual RazorSyntaxTree Parse(RazorSourceDocument source)
public virtual RazorSyntaxTree Parse(string input) => Parse(((ITextDocument)new SeekableTextReader(input)));
public virtual RazorSyntaxTree Parse(char[] input) => Parse(((ITextDocument)new SeekableTextReader(input)));
public virtual RazorSyntaxTree Parse(ITextDocument input) => ParseCore(input);
private RazorSyntaxTree ParseCore(ITextDocument input)
{ {
var context = new ParserContext(input, Options.DesignTimeMode); if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
var chars = new char[source.Length];
source.CopyTo(0, chars, 0, source.Length);
var reader = new SeekableTextReader(chars);
var context = new ParserContext(reader, Options.DesignTimeMode);
var codeParser = new CSharpCodeParser(Options.Directives, context); var codeParser = new CSharpCodeParser(Options.Directives, context);
var markupParser = new HtmlMarkupParser(context); var markupParser = new HtmlMarkupParser(context);
@ -47,7 +48,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var root = context.Builder.Build(); var root = context.Builder.Build();
var diagnostics = context.ErrorSink.Errors; var diagnostics = context.ErrorSink.Errors;
return RazorSyntaxTree.Create(root, diagnostics, Options); return RazorSyntaxTree.Create(root, source, diagnostics, Options);
} }
} }
} }

View File

@ -3,6 +3,7 @@
using System; using System;
using Microsoft.AspNetCore.Razor.Evolution.Intermediate; using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
using System.Collections.Generic;
namespace Microsoft.AspNetCore.Razor.Evolution namespace Microsoft.AspNetCore.Razor.Evolution
{ {
@ -28,6 +29,46 @@ namespace Microsoft.AspNetCore.Razor.Evolution
document.Items[typeof(RazorSyntaxTree)] = syntaxTree; document.Items[typeof(RazorSyntaxTree)] = syntaxTree;
} }
public static IReadOnlyList<RazorSyntaxTree> GetImportSyntaxTrees(this RazorCodeDocument document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return (document.Items[typeof(ImportSyntaxTreesHolder)] as ImportSyntaxTreesHolder)?.SyntaxTrees;
}
public static void SetImportSyntaxTrees(this RazorCodeDocument document, IReadOnlyList<RazorSyntaxTree> syntaxTrees)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
document.Items[typeof(ImportSyntaxTreesHolder)] = new ImportSyntaxTreesHolder(syntaxTrees);
}
public static IReadOnlyList<RazorSyntaxTree> GetIncludeSyntaxTrees(this RazorCodeDocument document)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
return (document.Items[typeof(IncludeSyntaxTreesHolder)] as IncludeSyntaxTreesHolder)?.SyntaxTrees;
}
public static void SetIncludeSyntaxTrees(this RazorCodeDocument document, IReadOnlyList<RazorSyntaxTree> syntaxTrees)
{
if (document == null)
{
throw new ArgumentNullException(nameof(document));
}
document.Items[typeof(IncludeSyntaxTreesHolder)] = new IncludeSyntaxTreesHolder(syntaxTrees);
}
public static DocumentIRNode GetIRDocument(this RazorCodeDocument document) public static DocumentIRNode GetIRDocument(this RazorCodeDocument document)
{ {
if (document == null) if (document == null)
@ -67,5 +108,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution
document.Items[typeof(RazorCSharpDocument)] = csharp; document.Items[typeof(RazorCSharpDocument)] = csharp;
} }
private class ImportSyntaxTreesHolder
{
public ImportSyntaxTreesHolder(IReadOnlyList<RazorSyntaxTree> syntaxTrees)
{
SyntaxTrees = syntaxTrees;
}
public IReadOnlyList<RazorSyntaxTree> SyntaxTrees { get; }
}
private class IncludeSyntaxTreesHolder
{
public IncludeSyntaxTreesHolder(IReadOnlyList<RazorSyntaxTree> syntaxTrees)
{
SyntaxTrees = syntaxTrees;
}
public IReadOnlyList<RazorSyntaxTree> SyntaxTrees { get; }
}
} }
} }

View File

@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
{ {
internal static RazorSyntaxTree Create( internal static RazorSyntaxTree Create(
Block root, Block root,
RazorSourceDocument source,
IEnumerable<RazorError> diagnostics, IEnumerable<RazorError> diagnostics,
RazorParserOptions options) RazorParserOptions options)
{ {
@ -19,6 +20,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
throw new ArgumentNullException(nameof(root)); throw new ArgumentNullException(nameof(root));
} }
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (diagnostics == null) if (diagnostics == null)
{ {
throw new ArgumentNullException(nameof(diagnostics)); throw new ArgumentNullException(nameof(diagnostics));
@ -29,7 +35,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
throw new ArgumentNullException(nameof(options)); throw new ArgumentNullException(nameof(options));
} }
return new DefaultRazorSyntaxTree(root, new List<RazorError>(diagnostics), options); return new DefaultRazorSyntaxTree(root, source, new List<RazorError>(diagnostics), options);
} }
public static RazorSyntaxTree Parse(RazorSourceDocument source) public static RazorSyntaxTree Parse(RazorSourceDocument source)
@ -50,10 +56,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
} }
var parser = new RazorParser(options ?? RazorParserOptions.CreateDefaultOptions()); var parser = new RazorParser(options ?? RazorParserOptions.CreateDefaultOptions());
var sourceContent = new char[source.Length]; return parser.Parse(source);
source.CopyTo(0, sourceContent, 0, source.Length);
return parser.Parse(sourceContent);
} }
internal abstract IReadOnlyList<RazorError> Diagnostics { get; } internal abstract IReadOnlyList<RazorError> Diagnostics { get; }
@ -61,5 +64,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
public abstract RazorParserOptions Options { get; } public abstract RazorParserOptions Options { get; }
internal abstract Block Root { get; } internal abstract Block Root { get; }
public abstract RazorSourceDocument Source { get; }
} }
} }

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
if (errorSink.Errors.Count > 0) if (errorSink.Errors.Count > 0)
{ {
var combinedErrors = CombineErrors(syntaxTree.Diagnostics, errorSink.Errors); var combinedErrors = CombineErrors(syntaxTree.Diagnostics, errorSink.Errors);
var erroredTree = RazorSyntaxTree.Create(syntaxTree.Root, combinedErrors, syntaxTree.Options); var erroredTree = RazorSyntaxTree.Create(syntaxTree.Root, syntaxTree.Source, combinedErrors, syntaxTree.Options);
return erroredTree; return erroredTree;
} }
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
diagnostics = CombineErrors(diagnostics, errorSink.Errors); diagnostics = CombineErrors(diagnostics, errorSink.Errors);
} }
var newSyntaxTree = RazorSyntaxTree.Create(rewrittenRoot, diagnostics, syntaxTree.Options); var newSyntaxTree = RazorSyntaxTree.Create(rewrittenRoot, syntaxTree.Source, diagnostics, syntaxTree.Options);
return newSyntaxTree; return newSyntaxTree;
} }

View File

@ -106,7 +106,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var sourceDocument = TestRazorSourceDocument.Create(content); var sourceDocument = TestRazorSourceDocument.Create(content);
var codeDocument = RazorCodeDocument.Create(sourceDocument); var codeDocument = RazorCodeDocument.Create(sourceDocument);
var originalTree = RazorSyntaxTree.Parse(sourceDocument); var originalTree = RazorSyntaxTree.Parse(sourceDocument);
var erroredOriginalTree = RazorSyntaxTree.Create(originalTree.Root, new[] { expectedErrors[0] }, originalTree.Options); var erroredOriginalTree = RazorSyntaxTree.Create(
originalTree.Root,
originalTree.Source,
new[] { expectedErrors[0] },
originalTree.Options);
// Act // Act
var outputTree = pass.Execute(codeDocument, erroredOriginalTree); var outputTree = pass.Execute(codeDocument, erroredOriginalTree);

View File

@ -46,6 +46,46 @@ namespace Microsoft.AspNetCore.Razor.Evolution
Assert.Equal("test_directive", directive.Name); Assert.Equal("test_directive", directive.Name);
} }
[Fact]
public void Execute_ParsesIncludesAndImports()
{
// Arrange
var phase = new DefaultRazorParsingPhase();
var engine = RazorEngine.CreateEmpty((b) =>
{
b.Phases.Add(phase);
b.Features.Add(new MyConfigureParserOptions());
});
var imports = new[]
{
TestRazorSourceDocument.Create(),
TestRazorSourceDocument.Create(),
};
var includes = new[]
{
TestRazorSourceDocument.Create(),
TestRazorSourceDocument.Create(),
};
var codeDocument = TestRazorCodeDocument.Create(TestRazorSourceDocument.Create(), imports, includes);
// Act
phase.Execute(codeDocument);
// Assert
Assert.Collection(
codeDocument.GetImportSyntaxTrees(),
t => { Assert.Same(t.Source, imports[0]); Assert.Equal("test_directive", Assert.Single(t.Options.Directives).Name); },
t => { Assert.Same(t.Source, imports[1]); Assert.Equal("test_directive", Assert.Single(t.Options.Directives).Name); });
Assert.Collection(
codeDocument.GetIncludeSyntaxTrees(),
t => { Assert.Same(t.Source, includes[0]); Assert.Equal("test_directive", Assert.Single(t.Options.Directives).Name); },
t => { Assert.Same(t.Source, includes[1]); Assert.Equal("test_directive", Assert.Single(t.Options.Directives).Name); });
}
private class MyConfigureParserOptions : IRazorConfigureParserFeature private class MyConfigureParserOptions : IRazorConfigureParserFeature
{ {
public RazorEngine Engine { get; set; } public RazorEngine Engine { get; set; }

View File

@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var parser = new RazorParser(options); var parser = new RazorParser(options);
var tree = parser.Parse((ITextDocument)reader); var tree = parser.Parse(TestRazorSourceDocument.Create(document));
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass(); var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();
tree = defaultDirectivePass.Execute(codeDocument: null, syntaxTree: tree); tree = defaultDirectivePass.Execute(codeDocument: null, syntaxTree: tree);
@ -51,6 +51,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
internal virtual RazorSyntaxTree ParseHtmlBlock(string document, bool designTime = false) internal virtual RazorSyntaxTree ParseHtmlBlock(string document, bool designTime = false)
{ {
var source = TestRazorSourceDocument.Create(document);
using (var reader = new SeekableTextReader(document)) using (var reader = new SeekableTextReader(document))
{ {
var context = new ParserContext(reader, designTime); var context = new ParserContext(reader, designTime);
@ -68,7 +70,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var options = RazorParserOptions.CreateDefaultOptions(); var options = RazorParserOptions.CreateDefaultOptions();
options.DesignTimeMode = designTime; options.DesignTimeMode = designTime;
return RazorSyntaxTree.Create(root, diagnostics, options); return RazorSyntaxTree.Create(root, source, diagnostics, options);
} }
} }
@ -82,6 +84,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
IEnumerable<DirectiveDescriptor> descriptors, IEnumerable<DirectiveDescriptor> descriptors,
bool designTime) bool designTime)
{ {
var source = TestRazorSourceDocument.Create(document);
using (var reader = new SeekableTextReader(document)) using (var reader = new SeekableTextReader(document))
{ {
var context = new ParserContext(reader, designTime); var context = new ParserContext(reader, designTime);
@ -106,7 +110,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
options.Directives.Add(directive); options.Directives.Add(directive);
} }
return RazorSyntaxTree.Create(root, diagnostics, options); return RazorSyntaxTree.Create(root, source, diagnostics, options);
} }
} }

View File

@ -13,9 +13,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{ {
var parser = new RazorParser(); var parser = new RazorParser();
var sourceDocument = TestRazorSourceDocument.CreateResource("TestFiles/Source/BasicMarkup.cshtml"); var sourceDocument = TestRazorSourceDocument.CreateResource("TestFiles/Source/BasicMarkup.cshtml");
var sourceContent = new char[sourceDocument.Length]; var output = parser.Parse(sourceDocument);
sourceDocument.CopyTo(0, sourceContent, 0, sourceDocument.Length);
var output = parser.Parse(sourceContent);
Assert.NotNull(output); Assert.NotNull(output);
} }
@ -29,7 +27,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var parser = new RazorParser(); var parser = new RazorParser();
// Act/Assert // Act/Assert
ParserTestBase.EvaluateResults(parser.Parse(new StringReader("foo @bar baz")), ParserTestBase.EvaluateResults(parser.Parse(TestRazorSourceDocument.Create("foo @bar baz")),
new MarkupBlock( new MarkupBlock(
factory.Markup("foo "), factory.Markup("foo "),
new ExpressionBlock( new ExpressionBlock(
@ -49,7 +47,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var parser = new RazorParser(); var parser = new RazorParser();
// Act // Act
var results = parser.Parse(new StringReader("foo @bar baz")); var results = parser.Parse(TestRazorSourceDocument.Create("foo @bar baz"));
// Assert // Assert
ParserTestBase.EvaluateResults(results, ParserTestBase.EvaluateResults(results,

View File

@ -39,6 +39,38 @@ namespace Microsoft.AspNetCore.Razor.Evolution
Assert.Same(expected, codeDocument.Items[typeof(RazorSyntaxTree)]); Assert.Same(expected, codeDocument.Items[typeof(RazorSyntaxTree)]);
} }
[Fact]
public void GetAndSetImportSyntaxTrees_ReturnsSyntaxTrees()
{
// Arrange
var codeDocument = TestRazorCodeDocument.CreateEmpty();
var expected = new[] { RazorSyntaxTree.Parse(codeDocument.Source), };
codeDocument.SetImportSyntaxTrees(expected);
// Act
var actual = codeDocument.GetImportSyntaxTrees();
// Assert
Assert.Same(expected, actual);
}
[Fact]
public void GetAndSetIncludeSyntaxTrees_ReturnsSyntaxTrees()
{
// Arrange
var codeDocument = TestRazorCodeDocument.CreateEmpty();
var expected = new[] { RazorSyntaxTree.Parse(codeDocument.Source), };
codeDocument.SetIncludeSyntaxTrees(expected);
// Act
var actual = codeDocument.GetIncludeSyntaxTrees();
// Assert
Assert.Same(expected, actual);
}
[Fact] [Fact]
public void GetIRDocument_ReturnsIRDocument() public void GetIRDocument_ReturnsIRDocument()
{ {

View File

@ -145,7 +145,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
var codeDocument = RazorCodeDocument.Create(sourceDocument); var codeDocument = RazorCodeDocument.Create(sourceDocument);
var originalTree = RazorSyntaxTree.Parse(sourceDocument); var originalTree = RazorSyntaxTree.Parse(sourceDocument);
var initialError = new RazorError("Initial test error", SourceLocation.Zero, length: 1); var initialError = new RazorError("Initial test error", SourceLocation.Zero, length: 1);
var erroredOriginalTree = RazorSyntaxTree.Create(originalTree.Root, new[] { initialError }, originalTree.Options); var erroredOriginalTree = RazorSyntaxTree.Create(
originalTree.Root,
originalTree.Source,
new[] { initialError },
originalTree.Options);
// Act // Act
var outputTree = pass.Execute(codeDocument, erroredOriginalTree); var outputTree = pass.Execute(codeDocument, erroredOriginalTree);
@ -194,7 +198,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
LegacyResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper("form"), LegacyResources.FormatTagHelpersParseTreeRewriter_FoundMalformedTagHelper("form"),
new SourceLocation(Environment.NewLine.Length * 2 + 30, 2, 1), new SourceLocation(Environment.NewLine.Length * 2 + 30, 2, 1),
length: 4); length: 4);
var erroredOriginalTree = RazorSyntaxTree.Create(originalTree.Root, new[] { initialError }, originalTree.Options); var erroredOriginalTree = RazorSyntaxTree.Create(originalTree.Root, originalTree.Source, new[] { initialError }, originalTree.Options);
// Act // Act
var outputTree = pass.Execute(codeDocument, erroredOriginalTree); var outputTree = pass.Execute(codeDocument, erroredOriginalTree);