Set file path from RazorSourceDocument in syntax tree

This commit is contained in:
Ajay Bhargav Baaskaran 2017-03-21 17:19:21 -07:00
parent 6fbbd5d148
commit cab6eea663
14 changed files with 91 additions and 40 deletions

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public virtual IEnumerable<TSymbol> TokenizeString(SourceLocation start, string input) public virtual IEnumerable<TSymbol> TokenizeString(SourceLocation start, string input)
{ {
using (var reader = new SeekableTextReader(input)) using (var reader = new SeekableTextReader(input, start.FilePath))
{ {
var tok = CreateTokenizer(reader); var tok = CreateTokenizer(reader);
TSymbol sym; TSymbol sym;

View File

@ -11,20 +11,23 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
internal class LineTrackingStringBuffer internal class LineTrackingStringBuffer
{ {
private readonly IList<TextLine> _lines; private readonly IList<TextLine> _lines;
private readonly string _filePath;
private TextLine _currentLine; private TextLine _currentLine;
private TextLine _endLine; private TextLine _endLine;
public LineTrackingStringBuffer(string content) public LineTrackingStringBuffer(string content, string filePath)
: this(content.ToCharArray()) : this(content.ToCharArray(), filePath)
{ {
} }
public LineTrackingStringBuffer(char[] content) public LineTrackingStringBuffer(char[] content, string filePath)
{ {
_endLine = new TextLine(0, 0); _endLine = new TextLine(0, 0);
_lines = new List<TextLine>() { _endLine }; _lines = new List<TextLine>() { _endLine };
Append(content); Append(content);
_filePath = filePath;
} }
public int Length public int Length
@ -34,7 +37,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
public SourceLocation EndLocation public SourceLocation EndLocation
{ {
get { return new SourceLocation(Length, _lines.Count - 1, _lines[_lines.Count - 1].Length); } get { return new SourceLocation(_filePath, Length, _lines.Count - 1, _lines[_lines.Count - 1].Length); }
} }
public CharacterReference CharAt(int absoluteIndex) public CharacterReference CharAt(int absoluteIndex)
@ -45,7 +48,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
throw new ArgumentOutOfRangeException(nameof(absoluteIndex)); throw new ArgumentOutOfRangeException(nameof(absoluteIndex));
} }
var idx = absoluteIndex - line.Start; var idx = absoluteIndex - line.Start;
return new CharacterReference(line.Content[idx], new SourceLocation(absoluteIndex, line.Index, idx)); return new CharacterReference(line.Content[idx], new SourceLocation(_filePath, absoluteIndex, line.Index, idx));
} }
private void Append(char[] content) private void Append(char[] content)

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
var chars = new char[source.Length]; var chars = new char[source.Length];
source.CopyTo(0, chars, 0, source.Length); source.CopyTo(0, chars, 0, source.Length);
var reader = new SeekableTextReader(chars); var reader = new SeekableTextReader(chars, source.FileName);
var context = new ParserContext(reader, Options.DesignTimeMode); var context = new ParserContext(reader, Options.DesignTimeMode);

View File

@ -10,20 +10,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{ {
private readonly LineTrackingStringBuffer _buffer; private readonly LineTrackingStringBuffer _buffer;
private int _position = 0; private int _position = 0;
private SourceLocation _location = SourceLocation.Zero; private SourceLocation _location;
private char? _current; private char? _current;
public SeekableTextReader(string source) : this(source.ToCharArray()) { } public SeekableTextReader(string source, string filePath) : this(source.ToCharArray(), filePath) { }
public SeekableTextReader(char[] source) public SeekableTextReader(char[] source, string filePath)
{ {
if (source == null) if (source == null)
{ {
throw new ArgumentNullException(nameof(source)); throw new ArgumentNullException(nameof(source));
} }
_buffer = new LineTrackingStringBuffer(source); _buffer = new LineTrackingStringBuffer(source, filePath);
UpdateState(); UpdateState();
_location = new SourceLocation(filePath, 0, 0, 0);
} }
public SourceLocation Location => _location; public SourceLocation Location => _location;

View File

@ -64,7 +64,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
@section Baz { @section Baz {
} }
}"; }";
var sourceDocument = TestRazorSourceDocument.Create(content); var sourceDocument = TestRazorSourceDocument.Create(content, fileName: null);
var codeDocument = RazorCodeDocument.Create(sourceDocument); var codeDocument = RazorCodeDocument.Create(sourceDocument);
var originalTree = RazorSyntaxTree.Parse(sourceDocument); var originalTree = RazorSyntaxTree.Parse(sourceDocument);
@ -106,7 +106,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
@section Baz { @section Baz {
} }
}"; }";
var sourceDocument = TestRazorSourceDocument.Create(content); var sourceDocument = TestRazorSourceDocument.Create(content, fileName: null);
var codeDocument = RazorCodeDocument.Create(sourceDocument); var codeDocument = RazorCodeDocument.Create(sourceDocument);
var originalTree = RazorSyntaxTree.Parse(sourceDocument); var originalTree = RazorSyntaxTree.Parse(sourceDocument);
var erroredOriginalTree = RazorSyntaxTree.Create( var erroredOriginalTree = RazorSyntaxTree.Create(

View File

@ -78,11 +78,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Arrange // Arrange
var engine = RazorEngine.Create(); var engine = RazorEngine.Create();
var document = RazorCodeDocument.Create(TestRazorSourceDocument.Create("@!!!")); var document = RazorCodeDocument.Create(TestRazorSourceDocument.Create("@!!!", fileName: "test.cshtml"));
var expected = RazorDiagnostic.Create(new RazorError( var expected = RazorDiagnostic.Create(new RazorError(
LegacyResources.FormatParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS("!"), LegacyResources.FormatParseError_Unexpected_Character_At_Start_Of_CodeBlock_CS("!"),
new SourceLocation(1, 0, 1), new SourceLocation("test.cshtml", 1, 0, 1),
length: 1)); length: 1));
// Act // Act
@ -100,11 +100,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
// Arrange // Arrange
var engine = RazorEngine.CreateDesignTime(); var engine = RazorEngine.CreateDesignTime();
var document = RazorCodeDocument.Create(TestRazorSourceDocument.Create("@{")); var document = RazorCodeDocument.Create(TestRazorSourceDocument.Create("@{", fileName: "test.cshtml"));
var expected = RazorDiagnostic.Create(new RazorError( var expected = RazorDiagnostic.Create(new RazorError(
LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(LegacyResources.BlockName_Code, "}", "{"), LegacyResources.FormatParseError_Expected_EndOfBlock_Before_EOF(LegacyResources.BlockName_Code, "}", "{"),
new SourceLocation(1, 0, 1), new SourceLocation("test.cshtml", 1, 0, 1),
length: 1)); length: 1));
// Act // Act

View File

@ -10,17 +10,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
[Fact] [Fact]
public void CtorInitializesProperties() public void CtorInitializesProperties()
{ {
var buffer = new LineTrackingStringBuffer(string.Empty); var buffer = new LineTrackingStringBuffer(string.Empty, "test.cshtml");
Assert.Equal(0, buffer.Length); Assert.Equal(0, buffer.Length);
} }
[Fact] [Fact]
public void CharAtCorrectlyReturnsLocation() public void CharAtCorrectlyReturnsLocation()
{ {
var buffer = new LineTrackingStringBuffer("foo\rbar\nbaz\r\nbiz"); var buffer = new LineTrackingStringBuffer("foo\rbar\nbaz\r\nbiz", "test.cshtml");
LineTrackingStringBuffer.CharacterReference chr = buffer.CharAt(14); var chr = buffer.CharAt(14);
Assert.Equal('i', chr.Character); Assert.Equal('i', chr.Character);
Assert.Equal(new SourceLocation(14, 3, 1), chr.Location); Assert.Equal(new SourceLocation("test.cshtml", 14, 3, 1), chr.Location);
} }
} }
} }

View File

@ -34,26 +34,36 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
internal virtual RazorSyntaxTree ParseDocument(string document, bool designTime = false) internal virtual RazorSyntaxTree ParseDocument(string document, bool designTime = false)
{ {
using (var reader = new SeekableTextReader(document)) var source = TestRazorSourceDocument.Create(document);
{ var reader = new SeekableTextReader(document, filePath: null);
var options = RazorParserOptions.CreateDefaultOptions();
options.DesignTimeMode = designTime;
var parser = new RazorParser(options); var context = new ParserContext(reader, designTime);
var tree = parser.Parse(TestRazorSourceDocument.Create(document)); var codeParser = new CSharpCodeParser(context);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass(); var markupParser = new HtmlMarkupParser(context);
tree = defaultDirectivePass.Execute(codeDocument: null, syntaxTree: tree);
return tree; codeParser.HtmlParser = markupParser;
} markupParser.CodeParser = codeParser;
markupParser.ParseDocument();
var root = context.Builder.Build();
var diagnostics = context.ErrorSink.Errors?.Select(error => RazorDiagnostic.Create(error));
var options = RazorParserOptions.CreateDefaultOptions();
options.DesignTimeMode = designTime;
var tree = RazorSyntaxTree.Create(root, source, diagnostics, options);
var defaultDirectivePass = new DefaultDirectiveSyntaxTreePass();
tree = defaultDirectivePass.Execute(codeDocument: null, syntaxTree: tree);
return tree;
} }
internal virtual RazorSyntaxTree ParseHtmlBlock(string document, bool designTime = false) internal virtual RazorSyntaxTree ParseHtmlBlock(string document, bool designTime = false)
{ {
var source = TestRazorSourceDocument.Create(document); var source = TestRazorSourceDocument.Create(document);
using (var reader = new SeekableTextReader(document)) using (var reader = new SeekableTextReader(document, filePath: null))
{ {
var context = new ParserContext(reader, designTime); var context = new ParserContext(reader, designTime);
@ -86,7 +96,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
{ {
var source = TestRazorSourceDocument.Create(document); var source = TestRazorSourceDocument.Create(document);
using (var reader = new SeekableTextReader(document)) using (var reader = new SeekableTextReader(document, filePath: null))
{ {
var context = new ParserContext(reader, designTime); var context = new ParserContext(reader, designTime);

View File

@ -227,7 +227,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private IEnumerable<ISymbol> Tokenize(string content, bool markup) private IEnumerable<ISymbol> Tokenize(string content, bool markup)
{ {
var tokenizer = MakeTokenizer(markup, new SeekableTextReader(content)); var tokenizer = MakeTokenizer(markup, new SeekableTextReader(content, filePath: null));
ISymbol symbol; ISymbol symbol;
ISymbol last = null; ISymbol last = null;

View File

@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
private class ExposedTokenizer : Tokenizer<CSharpSymbol, CSharpSymbolType> private class ExposedTokenizer : Tokenizer<CSharpSymbol, CSharpSymbolType>
{ {
public ExposedTokenizer(string input) public ExposedTokenizer(string input)
: base(new SeekableTextReader(input)) : base(new SeekableTextReader(input, filePath: null))
{ {
} }

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
// Arrange // Arrange
var success = true; var success = true;
var output = new StringBuilder(); var output = new StringBuilder();
using (var source = new SeekableTextReader(input)) using (var source = new SeekableTextReader(input, filePath: null))
{ {
var tokenizer = (Tokenizer<TSymbol, TSymbolType>)CreateTokenizer(source); var tokenizer = (Tokenizer<TSymbol, TSymbolType>)CreateTokenizer(source);
var counter = 0; var counter = 0;

View File

@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// 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.Collections.Generic;
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Razor.Evolution.Test namespace Microsoft.AspNetCore.Razor.Evolution.Test
@ -20,5 +22,39 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Test
Assert.NotNull(syntaxTree); Assert.NotNull(syntaxTree);
Assert.Empty(syntaxTree.Diagnostics); Assert.Empty(syntaxTree.Diagnostics);
} }
[Fact]
public void Parse_Persists_FilePath()
{
// Arrange
var filePath = "test.cshtml";
var source = TestRazorSourceDocument.Create("@if (true) { @if(false) { <div>@something.</div> } }", fileName: filePath);
// Act
var syntaxTree = RazorSyntaxTree.Parse(source);
// Assert
Assert.Empty(syntaxTree.Diagnostics);
Assert.NotNull(syntaxTree);
var spans = new List<SyntaxTreeNode>();
GetChildren(syntaxTree.Root);
Assert.All(spans, node => Assert.Equal(filePath, node.Start.FilePath));
void GetChildren(SyntaxTreeNode node)
{
if (node is Block block)
{
foreach (var child in block.Children)
{
GetChildren(child);
}
}
else
{
spans.Add(node);
}
}
}
} }
} }

View File

@ -385,7 +385,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
@addTagHelper *, TestAssembly @addTagHelper *, TestAssembly
<form> <form>
<input value='Hello' type='text' />"; <input value='Hello' type='text' />";
var sourceDocument = TestRazorSourceDocument.Create(content); var sourceDocument = TestRazorSourceDocument.Create(content, fileName: null);
var codeDocument = RazorCodeDocument.Create(sourceDocument); var codeDocument = RazorCodeDocument.Create(sourceDocument);
var originalTree = RazorSyntaxTree.Parse(sourceDocument); var originalTree = RazorSyntaxTree.Parse(sourceDocument);
@ -1334,7 +1334,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
<form> <form>
<input value='Hello' type='text' /> <input value='Hello' type='text' />
</form>"; </form>";
var sourceDocument = TestRazorSourceDocument.Create(content); var sourceDocument = TestRazorSourceDocument.Create(content, fileName: null);
return sourceDocument; return sourceDocument;
} }

View File

@ -52,14 +52,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
return stream; return stream;
} }
public static RazorSourceDocument Create(string content = "Hello, world!", Encoding encoding = null, bool normalizeNewLines = false) public static RazorSourceDocument Create(string content = "Hello, world!", Encoding encoding = null, bool normalizeNewLines = false, string fileName = "test.cshtml")
{ {
if (normalizeNewLines) if (normalizeNewLines)
{ {
content = NormalizeNewLines(content); content = NormalizeNewLines(content);
} }
return new TestRazorSourceDocument(content, encoding ?? Encoding.UTF8, "test.cshtml"); return new TestRazorSourceDocument(content, encoding ?? Encoding.UTF8, fileName);
} }
private static string NormalizeNewLines(string content) private static string NormalizeNewLines(string content)