Classify non-C# inside C#

Fixes a bug with preview formatting for FAR.

So when we ask the Roslyn API to classify C# for us, it will only
classify the actual C# tokens. We are responsible for filling in the
gaps and whitespace.

The bug is that the following text would have all of its whitespace
removed in the VS FAR preview window.

```
@{ var foo = "Hello, world!"; }
```

Would look like:
```
@{varfoo="Hello, world!";}
```

This fixes the issue and makes it look like what one would expect.
This commit is contained in:
Ryan Nowak 2018-10-27 20:53:25 -07:00
parent a3d0c8f634
commit 6c1bee1940
2 changed files with 113 additions and 1 deletions

View File

@ -176,6 +176,10 @@ namespace Microsoft.CodeAnalysis.Razor
secondaryDocument,
secondarySpan,
cancellationToken);
// NOTE: The Classifier will only returns spans for things that it understands. That means
// that whitespace is not classified. The preview expects us to provide contiguous spans,
// so we are going to have to fill in the gaps.
// Now we have to translate back to the primary document's coordinates.
var offset = primarySpan.Start - secondarySpan.Start;
@ -185,11 +189,29 @@ namespace Microsoft.CodeAnalysis.Razor
var updated = new TextSpan(classifiedSecondarySpan.TextSpan.Start + offset, classifiedSecondarySpan.TextSpan.Length);
Debug.Assert(primarySpan.Contains(updated));
// Make sure that we're not introducing a gap. Remember, we need to fill in the whitespace.
if (remainingSpan.Start < updated.Start)
{
builder.Add(new ClassifiedSpan(
ClassificationTypeNames.Text,
new TextSpan(remainingSpan.Start, updated.Start - remainingSpan.Start)));
remainingSpan = new TextSpan(updated.Start, remainingSpan.Length - (updated.Start - remainingSpan.Start));
}
builder.Add(new ClassifiedSpan(classifiedSecondarySpan.ClassificationType, updated));
remainingSpan = new TextSpan(updated.End, remainingSpan.Length - (updated.End - remainingSpan.Start));
}
// Make sure that we're not introducing a gap. Remember, we need to fill in the whitespace.
if (remainingSpan.Start < primarySpan.End)
{
builder.Add(new ClassifiedSpan(
ClassificationTypeNames.Text,
new TextSpan(remainingSpan.Start, primarySpan.End - remainingSpan.Start)));
remainingSpan = new TextSpan(primarySpan.End, remainingSpan.Length - (primarySpan.End - remainingSpan.Start));
}
remainingSpan = new TextSpan(primarySpan.End, remainingSpan.Length - primarySpan.Length);
}
// Deal with residue

View File

@ -70,22 +70,42 @@ namespace Microsoft.CodeAnalysis.Razor
Assert.Equal(@" var foo = ""Hello, World!"";", result.Value.Content.ToString(), ignoreLineEndingDifferences: true);
Assert.Collection(
result.Value.ClassifiedSpans,
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Keyword, c.ClassificationType);
Assert.Equal("var", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.LocalName, c.ClassificationType);
Assert.Equal("foo", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Operator, c.ClassificationType);
Assert.Equal("=", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.StringLiteral, c.ClassificationType);
Assert.Equal("\"Hello, World!\"", result.Value.Content.GetSubText(c.TextSpan).ToString());
@ -199,11 +219,21 @@ namespace Microsoft.CodeAnalysis.Razor
Assert.Equal("3", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Operator, c.ClassificationType);
Assert.Equal("+", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.NumericLiteral, c.ClassificationType);
Assert.Equal("4", result.Value.Content.GetSubText(c.TextSpan).ToString());
@ -219,11 +249,21 @@ namespace Microsoft.CodeAnalysis.Razor
Assert.Equal("foo", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Operator, c.ClassificationType);
Assert.Equal("+", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.LocalName, c.ClassificationType);
Assert.Equal("foo", result.Value.Content.GetSubText(c.TextSpan).ToString());
@ -292,21 +332,41 @@ namespace Microsoft.CodeAnalysis.Razor
ignoreLineEndingDifferences: true);
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal("\r\n ", result.Value.Content.GetSubText(c.TextSpan).ToString(), ignoreLineEndingDifferences: true);
},
c =>
{
Assert.Equal(ClassificationTypeNames.Keyword, c.ClassificationType);
Assert.Equal("var", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.LocalName, c.ClassificationType);
Assert.Equal("foo", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Operator, c.ClassificationType);
Assert.Equal("=", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.StringLiteral, c.ClassificationType);
Assert.Equal("\"Hello, World!\"", result.Value.Content.GetSubText(c.TextSpan).ToString());
@ -317,6 +377,11 @@ namespace Microsoft.CodeAnalysis.Razor
Assert.Equal(";", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal("\r\n", result.Value.Content.GetSubText(c.TextSpan).ToString(), ignoreLineEndingDifferences: true);
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(
@ -365,21 +430,41 @@ namespace Microsoft.CodeAnalysis.Razor
Assert.Equal("@{", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Keyword, c.ClassificationType);
Assert.Equal("var", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.LocalName, c.ClassificationType);
Assert.Equal("foo", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Operator, c.ClassificationType);
Assert.Equal("=", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.StringLiteral, c.ClassificationType);
Assert.Equal("\"Hello, World!\"", result.Value.Content.GetSubText(c.TextSpan).ToString());
@ -390,6 +475,11 @@ namespace Microsoft.CodeAnalysis.Razor
Assert.Equal(";", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal(" ", result.Value.Content.GetSubText(c.TextSpan).ToString());
},
c =>
{
Assert.Equal(ClassificationTypeNames.Text, c.ClassificationType);
Assert.Equal("}", result.Value.Content.GetSubText(c.TextSpan).ToString());