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:
parent
5229e65962
commit
a3d0c8f634
|
|
@ -37,6 +37,16 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
ExcerptMode mode,
|
ExcerptMode mode,
|
||||||
CancellationToken cancellationToken)
|
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)
|
if (_document == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -78,7 +88,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
// Now translate everything to be relative to the excerpt
|
// Now translate everything to be relative to the excerpt
|
||||||
var offset = 0 - excerptSpan.Start;
|
var offset = 0 - excerptSpan.Start;
|
||||||
var excerptText = primaryText.GetSubText(excerptSpan);
|
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++)
|
for (var i = 0; i < classifiedSpans.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
@ -89,24 +100,26 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
classifiedSpans[i] = new ClassifiedSpan(classifiedSpan.ClassificationType, updated);
|
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 startLine = primaryText.Lines.GetLineFromPosition(primarySpan.Start);
|
||||||
var endLine = primaryText.Lines.GetLineFromPosition(primarySpan.End);
|
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).
|
// 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);
|
return new TextSpan(startLine.Start, endLine.End - startLine.Start);
|
||||||
|
|
@ -188,5 +201,45 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
return builder;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ using Microsoft.CodeAnalysis.Host;
|
||||||
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using static Microsoft.CodeAnalysis.Razor.RazorDocumentExcerptService;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
|
|
@ -33,7 +34,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TryExcerptAsync_SingleLine_CanClassifyCSharp()
|
public async Task TryGetExcerptInternalAsync_SingleLine_CanClassifyCSharp()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (sourceText, primarySpan) = CreateText(
|
var (sourceText, primarySpan) = CreateText(
|
||||||
|
|
@ -53,13 +54,19 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
||||||
|
|
||||||
// Act
|
// 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
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(secondarySpan, result.Value.Span);
|
Assert.Equal(secondarySpan, result.Value.Span);
|
||||||
Assert.Same(secondary, result.Value.Document);
|
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.Equal(@" var foo = ""Hello, World!"";", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
result.Value.ClassifiedSpans,
|
result.Value.ClassifiedSpans,
|
||||||
|
|
@ -91,7 +98,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TryExcerptAsync_SingleLine_CanClassifyCSharp_ImplicitExpression()
|
public async Task TryGetExcerptInternalAsync_SingleLine_CanClassifyCSharp_ImplicitExpression()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (sourceText, primarySpan) = CreateText(
|
var (sourceText, primarySpan) = CreateText(
|
||||||
|
|
@ -111,13 +118,19 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
||||||
|
|
||||||
// Act
|
// 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
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(secondarySpan, result.Value.Span);
|
Assert.Equal(secondarySpan, result.Value.Span);
|
||||||
Assert.Same(secondary, result.Value.Document);
|
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.Equal(@" <body>@foo</body>", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
result.Value.ClassifiedSpans,
|
result.Value.ClassifiedSpans,
|
||||||
|
|
@ -139,7 +152,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TryExcerptAsync_SingleLine_CanClassifyCSharp_ComplexLine()
|
public async Task TryGetExcerptInternalAsync_SingleLine_CanClassifyCSharp_ComplexLine()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (sourceText, primarySpan) = CreateText(
|
var (sourceText, primarySpan) = CreateText(
|
||||||
|
|
@ -159,13 +172,19 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
||||||
|
|
||||||
// Act
|
// 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
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(secondarySpan, result.Value.Span);
|
Assert.Equal(secondarySpan, result.Value.Span);
|
||||||
Assert.Same(secondary, result.Value.Document);
|
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.Equal(@" <div>@(3 + 4)</div><div>@(foo + foo)</div>", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
result.Value.ClassifiedSpans,
|
result.Value.ClassifiedSpans,
|
||||||
|
|
@ -217,7 +236,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task TryExcerptAsync_MultiLine_CanClassifyCSharp()
|
public async Task TryGetExcerptInternalAsync_MultiLine_CanClassifyCSharp()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (sourceText, primarySpan) = CreateText(
|
var (sourceText, primarySpan) = CreateText(
|
||||||
|
|
@ -226,8 +245,8 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
@{
|
@{
|
||||||
var |foo| = ""Hello, World!"";
|
var |foo| = ""Hello, World!"";
|
||||||
}
|
}
|
||||||
<body>@foo</body>
|
<body></body>
|
||||||
<div>@(3 + 4)</div><div>@(foo + foo)</div>
|
<div></div>
|
||||||
</html>
|
</html>
|
||||||
");
|
");
|
||||||
|
|
||||||
|
|
@ -237,17 +256,27 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
||||||
|
|
||||||
// Act
|
// 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
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(secondarySpan, result.Value.Span);
|
Assert.Equal(secondarySpan, result.Value.Span);
|
||||||
Assert.Same(secondary, result.Value.Document);
|
Assert.Same(secondary, result.Value.Document);
|
||||||
|
|
||||||
|
// Verifies that the right part of the primary document will be highlighted.
|
||||||
Assert.Equal(
|
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!"";
|
var foo = ""Hello, World!"";
|
||||||
}",
|
}
|
||||||
|
<body></body>
|
||||||
|
<div></div>",
|
||||||
result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
||||||
|
|
||||||
Assert.Collection(
|
Assert.Collection(
|
||||||
|
|
@ -255,7 +284,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
c =>
|
c =>
|
||||||
{
|
{
|
||||||
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
|
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 =>
|
c =>
|
||||||
{
|
{
|
||||||
|
|
@ -285,12 +319,17 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
c =>
|
c =>
|
||||||
{
|
{
|
||||||
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
|
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]
|
[Fact]
|
||||||
public async Task TryExcerptAsync_MultiLine_Boundaries_CanClassifyCSharp()
|
public async Task TryGetExcerptInternalAsync_MultiLine_Boundaries_CanClassifyCSharp()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var (sourceText, primarySpan) = CreateText(@"@{ var |foo| = ""Hello, World!""; }");
|
var (sourceText, primarySpan) = CreateText(@"@{ var |foo| = ""Hello, World!""; }");
|
||||||
|
|
@ -301,13 +340,19 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
var secondarySpan = await GetSecondarySpanAsync(primary, primarySpan, secondary);
|
||||||
|
|
||||||
// Act
|
// 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
|
||||||
Assert.NotNull(result);
|
Assert.NotNull(result);
|
||||||
Assert.Equal(secondarySpan, result.Value.Span);
|
Assert.Equal(secondarySpan, result.Value.Span);
|
||||||
Assert.Same(secondary, result.Value.Document);
|
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(
|
Assert.Equal(
|
||||||
@"@{ var foo = ""Hello, World!""; }",
|
@"@{ var foo = ""Hello, World!""; }",
|
||||||
result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue