Move GetLanguageKind(...) implementation methods to a helper class

This commit is contained in:
Dustin Campbell 2024-09-04 16:32:19 -07:00
Родитель 07e138222c
Коммит e8eede823a
3 изменённых файлов: 165 добавлений и 153 удалений

Просмотреть файл

@ -8,7 +8,6 @@ using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.Legacy;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Razor.Logging;
@ -343,112 +342,14 @@ internal abstract class AbstractDocumentMappingService(IFilePathService filePath
public RazorLanguageKind GetLanguageKind(RazorCodeDocument codeDocument, int hostDocumentIndex, bool rightAssociative)
{
var classifiedSpans = GetClassifiedSpans(codeDocument);
var tagHelperSpans = GetTagHelperSpans(codeDocument);
var classifiedSpans = LanguageKindHelper.GetClassifiedSpans(codeDocument);
var tagHelperSpans = LanguageKindHelper.GetTagHelperSpans(codeDocument);
var documentLength = codeDocument.Source.Text.Length;
var languageKind = GetLanguageKindCore(classifiedSpans, tagHelperSpans, hostDocumentIndex, documentLength, rightAssociative);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, hostDocumentIndex, documentLength, rightAssociative);
return languageKind;
}
// 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,
};
}
}
private bool TryMapToHostDocumentRangeStrict(IRazorGeneratedDocument generatedDocument, LinePositionSpan generatedDocumentRange, out LinePositionSpan hostDocumentRange)
{
hostDocumentRange = default;
@ -666,36 +567,4 @@ internal abstract class AbstractDocumentMappingService(IFilePathService filePath
return sourceText.TryGetAbsoluteIndex(linePosition, out _);
}
}
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 = ClassifiedSpanVisitor.VisitRoot(syntaxTree);
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 = TagHelperSpanVisitor.VisitRoot(syntaxTree);
document.Items[typeof(TagHelperSpanInternal)] = tagHelperSpans;
}
return tagHelperSpans;
}
}

Просмотреть файл

@ -0,0 +1,143 @@
// 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,
};
}
}
}

Просмотреть файл

@ -713,7 +713,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 32 + Environment.NewLine.Length, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 32 + Environment.NewLine.Length, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);
@ -733,7 +733,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 42 + Environment.NewLine.Length, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 42 + Environment.NewLine.Length, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Razor, languageKind);
@ -759,7 +759,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 46 + Environment.NewLine.Length, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 46 + Environment.NewLine.Length, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -773,7 +773,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 5, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 5, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -787,7 +787,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 5, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 5, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);
@ -801,7 +801,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length + 1, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length + 1, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Razor, languageKind);
@ -829,7 +829,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var tagHelperSpans = ImmutableArray<TagHelperSpanInternal>.Empty;
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);
@ -843,7 +843,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);
@ -857,7 +857,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, text.Length, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -871,7 +871,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -885,7 +885,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 12, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 12, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -899,7 +899,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -913,7 +913,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 4, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 4, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -927,7 +927,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 1, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 1, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -941,7 +941,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 3, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 3, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -955,7 +955,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 2, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);
@ -969,7 +969,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 13, text.Length, rightAssociative: false);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 13, text.Length, rightAssociative: false);
// Assert
Assert.Equal(RazorLanguageKind.CSharp, languageKind);
@ -983,7 +983,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text);
// Act\
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 13, text.Length, rightAssociative: true);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 13, text.Length, rightAssociative: true);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);
@ -1005,7 +1005,7 @@ public class RazorDocumentMappingServiceTest(ITestOutputHelper testOutput) : Too
var (classifiedSpans, tagHelperSpans) = GetClassifiedSpans(text, new[] { descriptor.Build() });
// Act\
var languageKind = LspDocumentMappingService.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 40, text.Length, rightAssociative: true);
var languageKind = LanguageKindHelper.GetLanguageKindCore(classifiedSpans, tagHelperSpans, 40, text.Length, rightAssociative: true);
// Assert
Assert.Equal(RazorLanguageKind.Html, languageKind);