Fixes for excerpt service

We had a bug where were not returning the correct span for highlighting.
Fixed this.

Also we have a problem here, we're using types in our tests that are
coming from Roslyn - however we're not getting IVT for our test
assemblies. So some additional pain is required.
This commit is contained in:
Ryan Nowak 2018-10-24 20:20:32 -07:00
parent 5229e65962
commit a3d0c8f634
2 changed files with 123 additions and 25 deletions

View File

@ -37,6 +37,16 @@ namespace Microsoft.CodeAnalysis.Razor
ExcerptMode mode,
CancellationToken cancellationToken)
{
var result = await TryGetExcerptInternalAsync(document, span, (ExcerptModeInternal)mode, cancellationToken).ConfigureAwait(false);
return result?.ToExcerptResult();
}
public async Task<ExcerptResultInternal?> TryGetExcerptInternalAsync(
Document document,
TextSpan span,
ExcerptModeInternal mode,
CancellationToken cancellationToken)
{
if (_document == null)
{
return null;
@ -78,7 +88,8 @@ namespace Microsoft.CodeAnalysis.Razor
// Now translate everything to be relative to the excerpt
var offset = 0 - excerptSpan.Start;
var excerptText = primaryText.GetSubText(excerptSpan);
excerptSpan = new TextSpan(excerptSpan.Start + offset, excerptSpan.Length);
excerptSpan = new TextSpan(0, excerptSpan.Length);
primarySpan = new TextSpan(primarySpan.Start + offset, primarySpan.Length);
for (var i = 0; i < classifiedSpans.Count; i++)
{
@ -89,24 +100,26 @@ namespace Microsoft.CodeAnalysis.Razor
classifiedSpans[i] = new ClassifiedSpan(classifiedSpan.ClassificationType, updated);
}
return new ExcerptResult(excerptText, excerptSpan, classifiedSpans.ToImmutable(), document, span);
return new ExcerptResultInternal(excerptText, primarySpan, classifiedSpans.ToImmutable(), document, span);
}
private TextSpan ChooseExcerptSpan(SourceText primaryText, TextSpan primarySpan, ExcerptMode mode)
private TextSpan ChooseExcerptSpan(SourceText primaryText, TextSpan primarySpan, ExcerptModeInternal mode)
{
var startLine = primaryText.Lines.GetLineFromPosition(primarySpan.Start);
var endLine = primaryText.Lines.GetLineFromPosition(primarySpan.End);
// If we're showing a single line then this will do. Otherwise expand the range by 1 in
// If we're showing a single line then this will do. Otherwise expand the range by 3 in
// each direction (if possible).
if (mode == ExcerptMode.Tooltip && startLine.LineNumber > 0)
if (mode == ExcerptModeInternal.Tooltip)
{
startLine = primaryText.Lines[startLine.LineNumber - 1];
var index = Math.Max(startLine.LineNumber - 3, 0);
startLine = primaryText.Lines[index];
}
if (mode == ExcerptMode.Tooltip && endLine.LineNumber < primaryText.Lines.Count - 1)
if (mode == ExcerptModeInternal.Tooltip)
{
endLine = primaryText.Lines[endLine.LineNumber + 1];
var index = Math.Min(endLine.LineNumber + 3, primaryText.Lines.Count - 1);
endLine = primaryText.Lines[index];
}
return new TextSpan(startLine.Start, endLine.End - startLine.Start);
@ -188,5 +201,45 @@ namespace Microsoft.CodeAnalysis.Razor
return builder;
}
// We have IVT access to the Roslyn APIs for product code, but not for testing.
public enum ExcerptModeInternal
{
SingleLine = ExcerptMode.SingleLine,
Tooltip = ExcerptMode.Tooltip,
}
// We have IVT access to the Roslyn APIs for product code, but not for testing.
public readonly struct ExcerptResultInternal
{
public readonly SourceText Content;
public readonly TextSpan MappedSpan;
public readonly ImmutableArray<ClassifiedSpan> ClassifiedSpans;
public readonly Document Document;
public readonly TextSpan Span;
public ExcerptResultInternal(
SourceText content,
TextSpan mappedSpan,
ImmutableArray<ClassifiedSpan> classifiedSpans,
Document document,
TextSpan span)
{
Content = content;
MappedSpan = mappedSpan;
ClassifiedSpans = classifiedSpans;
Document = document;
Span = span;
}
public ExcerptResult ToExcerptResult()
{
return new ExcerptResult(Content, MappedSpan, ClassifiedSpans, Document, Span);
}
}
}
}

View File

@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
using Microsoft.CodeAnalysis.Text;
using Xunit;
using static Microsoft.CodeAnalysis.Razor.RazorDocumentExcerptService;
namespace Microsoft.CodeAnalysis.Razor
{
@ -33,7 +34,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
[Fact]
public async Task TryExcerptAsync_SingleLine_CanClassifyCSharp()
public async Task TryGetExcerptInternalAsync_SingleLine_CanClassifyCSharp()
{
// Arrange
var (sourceText, primarySpan) = CreateText(
@ -53,13 +54,19 @@ namespace Microsoft.CodeAnalysis.Razor
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
// Act
var result = await service.TryExcerptAsync(secondary, secondarySpan, ExcerptMode.SingleLine, CancellationToken.None);
var result = await service.TryGetExcerptInternalAsync(secondary, secondarySpan, ExcerptModeInternal.SingleLine, CancellationToken.None);
// Assert
Assert.NotNull(result);
Assert.Equal(secondarySpan, result.Value.Span);
Assert.Same(secondary, result.Value.Document);
// Verifies that the right part of the primary document will be highlighted.
Assert.Equal(
(await secondary.GetTextAsync()).GetSubText(secondarySpan).ToString(),
result.Value.Content.GetSubText(result.Value.MappedSpan).ToString(),
ignoreLineEndingDifferences: true);
Assert.Equal(@" var foo = ""Hello, World!"";", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
Assert.Collection(
result.Value.ClassifiedSpans,
@ -91,7 +98,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
[Fact]
public async Task TryExcerptAsync_SingleLine_CanClassifyCSharp_ImplicitExpression()
public async Task TryGetExcerptInternalAsync_SingleLine_CanClassifyCSharp_ImplicitExpression()
{
// Arrange
var (sourceText, primarySpan) = CreateText(
@ -111,13 +118,19 @@ namespace Microsoft.CodeAnalysis.Razor
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
// Act
var result = await service.TryExcerptAsync(secondary, secondarySpan, ExcerptMode.SingleLine, CancellationToken.None);
var result = await service.TryGetExcerptInternalAsync(secondary, secondarySpan, ExcerptModeInternal.SingleLine, CancellationToken.None);
// Assert
Assert.NotNull(result);
Assert.Equal(secondarySpan, result.Value.Span);
Assert.Same(secondary, result.Value.Document);
// Verifies that the right part of the primary document will be highlighted.
Assert.Equal(
(await secondary.GetTextAsync()).GetSubText(secondarySpan).ToString(),
result.Value.Content.GetSubText(result.Value.MappedSpan).ToString(),
ignoreLineEndingDifferences: true);
Assert.Equal(@" <body>@foo</body>", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
Assert.Collection(
result.Value.ClassifiedSpans,
@ -139,7 +152,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
[Fact]
public async Task TryExcerptAsync_SingleLine_CanClassifyCSharp_ComplexLine()
public async Task TryGetExcerptInternalAsync_SingleLine_CanClassifyCSharp_ComplexLine()
{
// Arrange
var (sourceText, primarySpan) = CreateText(
@ -159,13 +172,19 @@ namespace Microsoft.CodeAnalysis.Razor
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
// Act
var result = await service.TryExcerptAsync(secondary, secondarySpan, ExcerptMode.SingleLine, CancellationToken.None);
var result = await service.TryGetExcerptInternalAsync(secondary, secondarySpan, ExcerptModeInternal.SingleLine, CancellationToken.None);
// Assert
Assert.NotNull(result);
Assert.Equal(secondarySpan, result.Value.Span);
Assert.Same(secondary, result.Value.Document);
// Verifies that the right part of the primary document will be highlighted.
Assert.Equal(
(await secondary.GetTextAsync()).GetSubText(secondarySpan).ToString(),
result.Value.Content.GetSubText(result.Value.MappedSpan).ToString(),
ignoreLineEndingDifferences: true);
Assert.Equal(@" <div>@(3 + 4)</div><div>@(foo + foo)</div>", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
Assert.Collection(
result.Value.ClassifiedSpans,
@ -217,7 +236,7 @@ namespace Microsoft.CodeAnalysis.Razor
}
[Fact]
public async Task TryExcerptAsync_MultiLine_CanClassifyCSharp()
public async Task TryGetExcerptInternalAsync_MultiLine_CanClassifyCSharp()
{
// Arrange
var (sourceText, primarySpan) = CreateText(
@ -226,8 +245,8 @@ namespace Microsoft.CodeAnalysis.Razor
@{
var |foo| = ""Hello, World!"";
}
<body>@foo</body>
<div>@(3 + 4)</div><div>@(foo + foo)</div>
<body></body>
<div></div>
</html>
");
@ -237,17 +256,27 @@ namespace Microsoft.CodeAnalysis.Razor
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
// Act
var result = await service.TryExcerptAsync(secondary, secondarySpan, ExcerptMode.Tooltip, CancellationToken.None);
var result = await service.TryGetExcerptInternalAsync(secondary, secondarySpan, ExcerptModeInternal.Tooltip, CancellationToken.None);
// Assert
Assert.NotNull(result);
Assert.Equal(secondarySpan, result.Value.Span);
Assert.Same(secondary, result.Value.Document);
// Verifies that the right part of the primary document will be highlighted.
Assert.Equal(
@"@{
(await secondary.GetTextAsync()).GetSubText(secondarySpan).ToString(),
result.Value.Content.GetSubText(result.Value.MappedSpan).ToString(),
ignoreLineEndingDifferences: true);
Assert.Equal(
@"
<html>
@{
var foo = ""Hello, World!"";
}",
}
<body></body>
<div></div>",
result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
Assert.Collection(
@ -255,7 +284,12 @@ namespace Microsoft.CodeAnalysis.Razor
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal("@{", result.Value.Content.GetSubText(c.TextSpan).ToString());
Assert.Equal(
@"
<html>
@{",
result.Value.Content.GetSubText(c.TextSpan).ToString(),
ignoreLineEndingDifferences: true);
},
c =>
{
@ -285,12 +319,17 @@ namespace Microsoft.CodeAnalysis.Razor
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal("}", result.Value.Content.GetSubText(c.TextSpan).ToString());
Assert.Equal(
@"}
<body></body>
<div></div>",
result.Value.Content.GetSubText(c.TextSpan).ToString(),
ignoreLineEndingDifferences: true);
});
}
[Fact]
public async Task TryExcerptAsync_MultiLine_Boundaries_CanClassifyCSharp()
public async Task TryGetExcerptInternalAsync_MultiLine_Boundaries_CanClassifyCSharp()
{
// Arrange
var (sourceText, primarySpan) = CreateText(@"@{ var |foo| = ""Hello, World!""; }");
@ -301,13 +340,19 @@ namespace Microsoft.CodeAnalysis.Razor
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
// Act
var result = await service.TryExcerptAsync(secondary, secondarySpan, ExcerptMode.Tooltip, CancellationToken.None);
var result = await service.TryGetExcerptInternalAsync(secondary, secondarySpan, ExcerptModeInternal.Tooltip, CancellationToken.None);
// Assert
Assert.NotNull(result);
Assert.Equal(secondarySpan, result.Value.Span);
Assert.Same(secondary, result.Value.Document);
// Verifies that the right part of the primary document will be highlighted.
Assert.Equal(
(await secondary.GetTextAsync()).GetSubText(secondarySpan).ToString(),
result.Value.Content.GetSubText(result.Value.MappedSpan).ToString(),
ignoreLineEndingDifferences: true);
Assert.Equal(
@"@{ var foo = ""Hello, World!""; }",
result.Value.Content.ToString(), ignoreLineEndingDifferences: true);