Fix a bug in the span mapping code

This wasn't quite doing the right thing, and it's simpler than we make
it to be.
This commit is contained in:
Ryan Nowak 2018-10-17 19:27:29 -07:00
parent bafe1b27ff
commit 4927525519
3 changed files with 78 additions and 22 deletions

View File

@ -11,7 +11,6 @@ namespace Microsoft.CodeAnalysis.Experiment
{
public SpanMapResult(Document document, LinePositionSpan linePositionSpan)
{
}
}

View File

@ -153,6 +153,10 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
results.Add(new SpanMapResult(document, linePositionSpan));
}
else
{
results.Add(null);
}
}
return Task.FromResult(results.ToImmutable());
@ -161,31 +165,30 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
// Internal for testing.
internal static bool TryGetLinePositionSpan(TextSpan span, SourceText source, RazorCSharpDocument output, out LinePositionSpan linePositionSpan)
{
for (var i = 0; i < output.SourceMappings.Count; i++)
var mappings = output.SourceMappings;
for (var i = 0; i < mappings.Count; i++)
{
var mapping = output.SourceMappings[i];
if (span.Length > mapping.GeneratedSpan.Length)
{
// If the length of the generated span is smaller they can't match. A C# expression
// won't cover multiple generated spans.
//
// This heuristic is useful in the Razor context to filter out zero-length
// spans.
continue;
}
var mapping = mappings[i];
var original = mapping.OriginalSpan.AsTextSpan();
var generated = mapping.GeneratedSpan.AsTextSpan();
if (!generated.Contains(span))
{
// If the search span isn't contained within the generated span, it is not a match.
// A C# identifier won't cover multiple generated spans.
continue;
}
var leftOffset = span.Start - generated.Start;
var rightOffset = span.End - generated.End;
if (leftOffset >= 0 && rightOffset <= 0)
{
// This span mapping contains the span.
var adjusted = new TextSpan(original.Start + leftOffset, (original.End + rightOffset) - (original.Start + leftOffset));
linePositionSpan = source.Lines.GetLinePositionSpan(adjusted);
return true;
}
Debug.Assert(leftOffset >= 0);
Debug.Assert(rightOffset <= 0);
// Note: we don't handle imports here - the assumption is that for all of the scenarios we
// support, the span is in the original source document.
var adjusted = new TextSpan(original.Start + leftOffset, (original.End + rightOffset) - (original.Start + leftOffset));
linePositionSpan = source.Lines.GetLinePositionSpan(adjusted);
return true;
}
linePositionSpan = default;

View File

@ -59,6 +59,62 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
Assert.NotNull(container.LatestDocument);
}
[Fact]
public void TryGetLinePositionSpan_SpanMatchesSourceMapping_ReturnsTrue()
{
// Arrange
var content = @"
@SomeProperty
";
var sourceText = SourceText.From(content);
var codeDocument = GetCodeDocument(content);
var csharpDocument = codeDocument.GetCSharpDocument();
var generatedCode = csharpDocument.GeneratedCode;
var symbol = "SomeProperty";
var span = new TextSpan(generatedCode.IndexOf(symbol), symbol.Length);
// Position of `SomeProperty` in the source code.
var expectedLineSpan = new LinePositionSpan(new LinePosition(1, 1), new LinePosition(1, 13));
// Act
var result = GeneratedCodeContainer.TryGetLinePositionSpan(span, sourceText, csharpDocument, out var lineSpan);
// Assert
Assert.True(result);
Assert.Equal(expectedLineSpan, lineSpan);
}
[Fact]
public void TryGetLinePositionSpan_SpanMatchesSourceMapping_MatchingOnPosition_ReturnsTrue()
{
// Arrange
var content = @"
@SomeProperty
@SomeProperty
@SomeProperty
";
var sourceText = SourceText.From(content);
var codeDocument = GetCodeDocument(content);
var csharpDocument = codeDocument.GetCSharpDocument();
var generatedCode = csharpDocument.GeneratedCode;
var symbol = "SomeProperty";
// Second occurrence
var span = new TextSpan(generatedCode.IndexOf(symbol, generatedCode.IndexOf(symbol) + symbol.Length), symbol.Length);
// Position of `SomeProperty` in the source code.
var expectedLineSpan = new LinePositionSpan(new LinePosition(2, 1), new LinePosition(2, 13));
// Act
var result = GeneratedCodeContainer.TryGetLinePositionSpan(span, sourceText, csharpDocument, out var lineSpan);
// Assert
Assert.True(result);
Assert.Equal(expectedLineSpan, lineSpan);
}
[Fact]
public void TryGetLinePositionSpan_SpanWithinSourceMapping_ReturnsTrue()
{
@ -73,8 +129,6 @@ namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
var csharpDocument = codeDocument.GetCSharpDocument();
var generatedCode = csharpDocument.GeneratedCode;
// TODO: Make writing these tests a little less manual.
// Position of `SomeProperty` in the generated code.
var symbol = "SomeProperty";
var span = new TextSpan(generatedCode.IndexOf(symbol), symbol.Length);