зеркало из https://github.com/dotnet/razor.git
Update GetLanguageKind(...) tests and move to Workspaces.Test
This commit is contained in:
Родитель
ed8e62b920
Коммит
5105d1b960
|
@ -1,143 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.DocumentMapping;
|
||||
|
||||
internal static class LanguageKindHelper
|
||||
{
|
||||
public static ImmutableArray<ClassifiedSpanInternal> GetClassifiedSpans(RazorCodeDocument document)
|
||||
{
|
||||
// Since this service is called so often, we get a good performance improvement by caching these values
|
||||
// for this code document. If the document changes, as the user types, then the document instance will be
|
||||
// different, so we don't need to worry about invalidating the cache.
|
||||
if (!document.Items.TryGetValue(typeof(ClassifiedSpanInternal), out ImmutableArray<ClassifiedSpanInternal> classifiedSpans))
|
||||
{
|
||||
var syntaxTree = document.GetSyntaxTree();
|
||||
classifiedSpans = ClassifiedSpanVisitor.VisitRoot(syntaxTree);
|
||||
|
||||
document.Items[typeof(ClassifiedSpanInternal)] = classifiedSpans;
|
||||
}
|
||||
|
||||
return classifiedSpans;
|
||||
}
|
||||
|
||||
public static ImmutableArray<TagHelperSpanInternal> GetTagHelperSpans(RazorCodeDocument document)
|
||||
{
|
||||
// Since this service is called so often, we get a good performance improvement by caching these values
|
||||
// for this code document. If the document changes, as the user types, then the document instance will be
|
||||
// different, so we don't need to worry about invalidating the cache.
|
||||
if (!document.Items.TryGetValue(typeof(TagHelperSpanInternal), out ImmutableArray<TagHelperSpanInternal> tagHelperSpans))
|
||||
{
|
||||
var syntaxTree = document.GetSyntaxTree();
|
||||
tagHelperSpans = TagHelperSpanVisitor.VisitRoot(syntaxTree);
|
||||
|
||||
document.Items[typeof(TagHelperSpanInternal)] = tagHelperSpans;
|
||||
}
|
||||
|
||||
return tagHelperSpans;
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
internal static RazorLanguageKind GetLanguageKindCore(
|
||||
ImmutableArray<ClassifiedSpanInternal> classifiedSpans,
|
||||
ImmutableArray<TagHelperSpanInternal> tagHelperSpans,
|
||||
int hostDocumentIndex,
|
||||
int hostDocumentLength,
|
||||
bool rightAssociative)
|
||||
{
|
||||
var length = classifiedSpans.Length;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var classifiedSpan = classifiedSpans[i];
|
||||
var span = classifiedSpan.Span;
|
||||
|
||||
if (span.AbsoluteIndex <= hostDocumentIndex)
|
||||
{
|
||||
var end = span.AbsoluteIndex + span.Length;
|
||||
if (end >= hostDocumentIndex)
|
||||
{
|
||||
if (end == hostDocumentIndex)
|
||||
{
|
||||
// We're at an edge.
|
||||
|
||||
if (classifiedSpan.SpanKind is SpanKindInternal.MetaCode or SpanKindInternal.Transition)
|
||||
{
|
||||
// If we're on an edge of a transition of some kind (MetaCode representing an open or closing piece of syntax such as <|,
|
||||
// and Transition representing an explicit transition to/from razor syntax, such as @|), prefer to classify to the span
|
||||
// to the right to better represent where the user clicks
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're right associative, then we don't want to use the classification that we're at the end
|
||||
// of, if we're also at the start of the next one
|
||||
if (rightAssociative)
|
||||
{
|
||||
if (i < classifiedSpans.Length - 1 && classifiedSpans[i + 1].Span.AbsoluteIndex == hostDocumentIndex)
|
||||
{
|
||||
// If we're at the start of the next span, then use that span
|
||||
return GetLanguageFromClassifiedSpan(classifiedSpans[i + 1]);
|
||||
}
|
||||
|
||||
// Otherwise, we did not find a match using right associativity, so check for tag helpers
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GetLanguageFromClassifiedSpan(classifiedSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var tagHelperSpan in tagHelperSpans)
|
||||
{
|
||||
var span = tagHelperSpan.Span;
|
||||
|
||||
if (span.AbsoluteIndex <= hostDocumentIndex)
|
||||
{
|
||||
var end = span.AbsoluteIndex + span.Length;
|
||||
if (end >= hostDocumentIndex)
|
||||
{
|
||||
if (end == hostDocumentIndex)
|
||||
{
|
||||
// We're at an edge. TagHelper spans never own their edge and aren't represented by marker spans
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found intersection
|
||||
return RazorLanguageKind.Html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the language of the last classified span if we're at the end
|
||||
// of the document.
|
||||
if (classifiedSpans.Length != 0 && hostDocumentIndex == hostDocumentLength)
|
||||
{
|
||||
var lastClassifiedSpan = classifiedSpans.Last();
|
||||
return GetLanguageFromClassifiedSpan(lastClassifiedSpan);
|
||||
}
|
||||
|
||||
// Default to Razor
|
||||
return RazorLanguageKind.Razor;
|
||||
|
||||
static RazorLanguageKind GetLanguageFromClassifiedSpan(ClassifiedSpanInternal classifiedSpan)
|
||||
{
|
||||
// Overlaps with request
|
||||
return classifiedSpan.SpanKind switch
|
||||
{
|
||||
SpanKindInternal.Markup => RazorLanguageKind.Html,
|
||||
SpanKindInternal.Code => RazorLanguageKind.CSharp,
|
||||
|
||||
// Content type was non-C# or Html or we couldn't find a classified span overlapping the request position.
|
||||
// All other classified span kinds default back to Razor
|
||||
_ => RazorLanguageKind.Razor,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,12 +2,13 @@
|
|||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.CodeAnalysis.Razor;
|
||||
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
@ -143,10 +144,139 @@ internal static class RazorCodeDocumentExtensions
|
|||
|
||||
public static RazorLanguageKind GetLanguageKind(this RazorCodeDocument codeDocument, int hostDocumentIndex, bool rightAssociative)
|
||||
{
|
||||
var classifiedSpans = LanguageKindHelper.GetClassifiedSpans(codeDocument);
|
||||
var tagHelperSpans = LanguageKindHelper.GetTagHelperSpans(codeDocument);
|
||||
var classifiedSpans = GetClassifiedSpans(codeDocument);
|
||||
var tagHelperSpans = GetTagHelperSpans(codeDocument);
|
||||
var documentLength = codeDocument.Source.Text.Length;
|
||||
|
||||
return LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, hostDocumentIndex, documentLength, rightAssociative);
|
||||
return GetLanguageKindCore(classifiedSpans, tagHelperSpans, hostDocumentIndex, documentLength, rightAssociative);
|
||||
}
|
||||
|
||||
private static ImmutableArray<ClassifiedSpanInternal> GetClassifiedSpans(RazorCodeDocument document)
|
||||
{
|
||||
// Since this service is called so often, we get a good performance improvement by caching these values
|
||||
// for this code document. If the document changes, as the user types, then the document instance will be
|
||||
// different, so we don't need to worry about invalidating the cache.
|
||||
if (!document.Items.TryGetValue(typeof(ClassifiedSpanInternal), out ImmutableArray<ClassifiedSpanInternal> classifiedSpans))
|
||||
{
|
||||
var syntaxTree = document.GetSyntaxTree();
|
||||
classifiedSpans = syntaxTree.GetClassifiedSpans();
|
||||
|
||||
document.Items[typeof(ClassifiedSpanInternal)] = classifiedSpans;
|
||||
}
|
||||
|
||||
return classifiedSpans;
|
||||
}
|
||||
|
||||
private static ImmutableArray<TagHelperSpanInternal> GetTagHelperSpans(RazorCodeDocument document)
|
||||
{
|
||||
// Since this service is called so often, we get a good performance improvement by caching these values
|
||||
// for this code document. If the document changes, as the user types, then the document instance will be
|
||||
// different, so we don't need to worry about invalidating the cache.
|
||||
if (!document.Items.TryGetValue(typeof(TagHelperSpanInternal), out ImmutableArray<TagHelperSpanInternal> tagHelperSpans))
|
||||
{
|
||||
var syntaxTree = document.GetSyntaxTree();
|
||||
tagHelperSpans = syntaxTree.GetTagHelperSpans();
|
||||
|
||||
document.Items[typeof(TagHelperSpanInternal)] = tagHelperSpans;
|
||||
}
|
||||
|
||||
return tagHelperSpans;
|
||||
}
|
||||
|
||||
private static RazorLanguageKind GetLanguageKindCore(
|
||||
ImmutableArray<ClassifiedSpanInternal> classifiedSpans,
|
||||
ImmutableArray<TagHelperSpanInternal> tagHelperSpans,
|
||||
int hostDocumentIndex,
|
||||
int hostDocumentLength,
|
||||
bool rightAssociative)
|
||||
{
|
||||
var length = classifiedSpans.Length;
|
||||
for (var i = 0; i < length; i++)
|
||||
{
|
||||
var classifiedSpan = classifiedSpans[i];
|
||||
var span = classifiedSpan.Span;
|
||||
|
||||
if (span.AbsoluteIndex <= hostDocumentIndex)
|
||||
{
|
||||
var end = span.AbsoluteIndex + span.Length;
|
||||
if (end >= hostDocumentIndex)
|
||||
{
|
||||
if (end == hostDocumentIndex)
|
||||
{
|
||||
// We're at an edge.
|
||||
|
||||
if (classifiedSpan.SpanKind is SpanKindInternal.MetaCode or SpanKindInternal.Transition)
|
||||
{
|
||||
// If we're on an edge of a transition of some kind (MetaCode representing an open or closing piece of syntax such as <|,
|
||||
// and Transition representing an explicit transition to/from razor syntax, such as @|), prefer to classify to the span
|
||||
// to the right to better represent where the user clicks
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're right associative, then we don't want to use the classification that we're at the end
|
||||
// of, if we're also at the start of the next one
|
||||
if (rightAssociative)
|
||||
{
|
||||
if (i < classifiedSpans.Length - 1 && classifiedSpans[i + 1].Span.AbsoluteIndex == hostDocumentIndex)
|
||||
{
|
||||
// If we're at the start of the next span, then use that span
|
||||
return GetLanguageFromClassifiedSpan(classifiedSpans[i + 1]);
|
||||
}
|
||||
|
||||
// Otherwise, we did not find a match using right associativity, so check for tag helpers
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GetLanguageFromClassifiedSpan(classifiedSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var tagHelperSpan in tagHelperSpans)
|
||||
{
|
||||
var span = tagHelperSpan.Span;
|
||||
|
||||
if (span.AbsoluteIndex <= hostDocumentIndex)
|
||||
{
|
||||
var end = span.AbsoluteIndex + span.Length;
|
||||
if (end >= hostDocumentIndex)
|
||||
{
|
||||
if (end == hostDocumentIndex)
|
||||
{
|
||||
// We're at an edge. TagHelper spans never own their edge and aren't represented by marker spans
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found intersection
|
||||
return RazorLanguageKind.Html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the language of the last classified span if we're at the end
|
||||
// of the document.
|
||||
if (classifiedSpans.Length != 0 && hostDocumentIndex == hostDocumentLength)
|
||||
{
|
||||
var lastClassifiedSpan = classifiedSpans.Last();
|
||||
return GetLanguageFromClassifiedSpan(lastClassifiedSpan);
|
||||
}
|
||||
|
||||
// Default to Razor
|
||||
return RazorLanguageKind.Razor;
|
||||
|
||||
static RazorLanguageKind GetLanguageFromClassifiedSpan(ClassifiedSpanInternal classifiedSpan)
|
||||
{
|
||||
// Overlaps with request
|
||||
return classifiedSpan.SpanKind switch
|
||||
{
|
||||
SpanKindInternal.Markup => RazorLanguageKind.Html,
|
||||
SpanKindInternal.Code => RazorLanguageKind.CSharp,
|
||||
|
||||
// Content type was non-C# or Html or we couldn't find a classified span overlapping the request position.
|
||||
// All other classified span kinds default back to Razor
|
||||
_ => RazorLanguageKind.Razor,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common.LanguageServer;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common.Workspaces;
|
||||
|
@ -699,339 +697,12 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
|
|||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_TagHelperElementOwnsName()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
var text = """
|
||||
@addTagHelper *, TestAssembly
|
||||
<test>@Name</test>
|
||||
""";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 32 + Environment.NewLine.Length, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_TagHelpersDoNotOwnTrailingEdge()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
var text = """
|
||||
@addTagHelper *, TestAssembly
|
||||
<test></test>@DateTime.Now
|
||||
""";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 42 + Environment.NewLine.Length, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Razor, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_TagHelperNestedCSharpAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.BindAttribute(builder =>
|
||||
{
|
||||
builder.Name = "asp-int";
|
||||
builder.TypeName = typeof(int).FullName;
|
||||
builder.SetMetadata(PropertyName("AspInt"));
|
||||
});
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
var text = """
|
||||
@addTagHelper *, TestAssembly
|
||||
<test asp-int='123'></test>
|
||||
""";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 46 + Environment.NewLine.Length, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_CSharp()
|
||||
{
|
||||
// Arrange
|
||||
var text = "<p>@Name</p>";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 5, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_Html()
|
||||
{
|
||||
// Arrange
|
||||
var text = "<p>Hello World</p>";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 5, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_DefaultsToRazorLanguageIfCannotLocateOwner()
|
||||
{
|
||||
// Arrange
|
||||
var text = "<p>Hello World</p>";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length + 1, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Razor, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_GetsLastClassifiedSpanLanguageIfAtEndOfDocument()
|
||||
{
|
||||
// Arrange
|
||||
var text = """
|
||||
<strong>Something</strong>
|
||||
<App>
|
||||
""";
|
||||
var classifiedSpans = ImmutableArray.Create<ClassifiedSpanInternal>(
|
||||
new(new SourceSpan(0, 0),
|
||||
blockSpan: new SourceSpan(absoluteIndex: 0, lineIndex: 0, characterIndex: 0, length: text.Length),
|
||||
SpanKindInternal.Transition,
|
||||
blockKind: default,
|
||||
acceptedCharacters: default),
|
||||
new(new SourceSpan(0, 26),
|
||||
blockSpan: default,
|
||||
SpanKindInternal.Markup,
|
||||
blockKind: default,
|
||||
acceptedCharacters: default));
|
||||
var tagHelperSpans = ImmutableArray<TagHelperSpanInternal>.Empty;
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_HtmlEdgeEnd()
|
||||
{
|
||||
// Arrange
|
||||
var text = "Hello World";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_CSharpEdgeEnd()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@Name";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_RazorEdgeWithCSharp()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@{}";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_CSharpEdgeWithCSharpMarker()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@{var x = 1;}";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 12, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_ExplicitExpressionStartCSharp()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@()";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_ExplicitExpressionInProgressCSharp()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@(Da)";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 4, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_ImplicitExpressionStartCSharp()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 1, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_ImplicitExpressionInProgressCSharp()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@Da";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 3, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_RazorEdgeWithHtml()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@{<br />}";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_HtmlInCSharpLeftAssociative()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@if (true) { <br /> }";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 13, text.Length, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_HtmlInCSharpRightAssociative()
|
||||
{
|
||||
// Arrange
|
||||
var text = "@if (true) { <br /> }";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
|
||||
|
||||
// Act\
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 13, text.Length, rightAssociative: true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKindCore_TagHelperInCSharpRightAssociative()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
var text = """
|
||||
@addTagHelper *, TestAssembly
|
||||
@if {
|
||||
<test>@Name</test>
|
||||
}
|
||||
""";
|
||||
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
|
||||
|
||||
// Act\
|
||||
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 40, text.Length, rightAssociative: true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
private static (ImmutableArray<ClassifiedSpanInternal> classifiedSpans, ImmutableArray<TagHelperSpanInternal> tagHelperSpans) GetClassifiedSpans(string text, IReadOnlyList<TagHelperDescriptor>? tagHelpers = null)
|
||||
{
|
||||
var codeDocument = CreateCodeDocument(text, tagHelpers);
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
var classifiedSpans = syntaxTree.GetClassifiedSpans();
|
||||
var tagHelperSpans = syntaxTree.GetTagHelperSpans();
|
||||
return (classifiedSpans, tagHelperSpans);
|
||||
}
|
||||
|
||||
private static RazorCodeDocument CreateCodeDocument(string text, IReadOnlyList<TagHelperDescriptor>? tagHelpers = null)
|
||||
{
|
||||
tagHelpers ??= Array.Empty<TagHelperDescriptor>();
|
||||
var sourceDocument = TestRazorSourceDocument.Create(text);
|
||||
var projectEngine = RazorProjectEngine.Create(builder => { });
|
||||
var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, "mvc", importSources: default, tagHelpers);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
private static RazorCodeDocument CreateCodeDocumentWithCSharpProjection(string razorSource, string projectedCSharpSource, ImmutableArray<SourceMapping> sourceMappings)
|
||||
{
|
||||
var codeDocument = CreateCodeDocument(razorSource, tagHelpers: []);
|
||||
var sourceDocument = TestRazorSourceDocument.Create(razorSource);
|
||||
var projectEngine = RazorProjectEngine.Create(builder => { });
|
||||
var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, "mvc", importSources: default, tagHelpers: []);
|
||||
|
||||
var csharpDocument = new RazorCSharpDocument(
|
||||
codeDocument,
|
||||
projectedCSharpSource,
|
||||
|
|
|
@ -0,0 +1,340 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Test.Common;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using static Microsoft.AspNetCore.Razor.Language.CommonMetadata;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Extensions;
|
||||
|
||||
public class RazorCodeDocumentExtensionsTest(ITestOutputHelper testOutput) : ToolingTestBase(testOutput)
|
||||
{
|
||||
[Fact]
|
||||
public void GetLanguageKind_TagHelperElementOwnsName()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
|
||||
TestCode code = """
|
||||
@addTagHelper *, TestAssembly
|
||||
<te$$st>@Name</test>
|
||||
""";
|
||||
|
||||
var codeDocument = CreateCodeDocument(code, descriptor.Build());
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_TagHelpersDoNotOwnTrailingEdge()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
|
||||
TestCode code = """
|
||||
@addTagHelper *, TestAssembly
|
||||
<test></test>$$@DateTime.Now
|
||||
""";
|
||||
|
||||
var codeDocument = CreateCodeDocument(code, descriptor.Build());
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Razor, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_TagHelperNestedCSharpAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.BindAttribute(builder =>
|
||||
{
|
||||
builder.Name = "asp-int";
|
||||
builder.TypeName = typeof(int).FullName;
|
||||
builder.SetMetadata(PropertyName("AspInt"));
|
||||
});
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
|
||||
TestCode code = """
|
||||
@addTagHelper *, TestAssembly
|
||||
<test asp-int='12$$3'></test>
|
||||
""";
|
||||
|
||||
var codeDocument = CreateCodeDocument(code, descriptor.Build());
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_CSharp()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "<p>@N$$ame</p>";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_Html()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "<p>He$$llo World</p>";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_DefaultsToRazorLanguageIfCannotLocateOwner()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "<p>Hello World</p>$$";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position + 1, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Razor, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_GetsLastClassifiedSpanLanguageIfAtEndOfDocument()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = """
|
||||
<strong>Something</strong>
|
||||
<App>$$
|
||||
""";
|
||||
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_HtmlEdgeEnd()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "Hello World$$";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_CSharpEdgeEnd()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@Name$$";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_RazorEdgeWithCSharp()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@{$$}";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_CSharpEdgeWithCSharpMarker()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@{var x = 1;$$}";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_ExplicitExpressionStartCSharp()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@($$)";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_ExplicitExpressionInProgressCSharp()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@(Da$$)";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_ImplicitExpressionStartCSharp()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@$$";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_ImplicitExpressionInProgressCSharp()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@Da$$";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_RazorEdgeWithHtml()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@{$$<br />}";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_HtmlInCSharpLeftAssociative()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@if (true) { $$<br /> }";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: false);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_HtmlInCSharpRightAssociative()
|
||||
{
|
||||
// Arrange
|
||||
TestCode code = "@if (true) { $$<br /> }";
|
||||
var codeDocument = CreateCodeDocument(code);
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetLanguageKind_TagHelperInCSharpRightAssociative()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = TagHelperDescriptorBuilder.Create("TestTagHelper", "TestAssembly");
|
||||
descriptor.TagMatchingRule(rule => rule.TagName = "test");
|
||||
descriptor.SetMetadata(TypeName("TestTagHelper"));
|
||||
|
||||
TestCode code = """
|
||||
@addTagHelper *, TestAssembly
|
||||
@if {
|
||||
$$<test>@Name</test>
|
||||
}
|
||||
""";
|
||||
|
||||
var codeDocument = CreateCodeDocument(code, descriptor.Build());
|
||||
|
||||
// Act
|
||||
var languageKind = codeDocument.GetLanguageKind(code.Position, rightAssociative: true);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(RazorLanguageKind.Html, languageKind);
|
||||
}
|
||||
|
||||
private static RazorCodeDocument CreateCodeDocument(TestCode code, params ImmutableArray<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
tagHelpers = tagHelpers.NullToEmpty();
|
||||
|
||||
var sourceDocument = TestRazorSourceDocument.Create(code.Text);
|
||||
var projectEngine = RazorProjectEngine.Create(builder => { });
|
||||
|
||||
return projectEngine.ProcessDesignTime(sourceDocument, "mvc", importSources: default, tagHelpers);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче