File: Extensions\RazorCodeDocumentExtensions_ComponentEndTag.cs
Web Access
Project: src\src\Razor\src\Razor\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj (Microsoft.CodeAnalysis.Razor.Workspaces)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Razor.Language.Syntax;
 
namespace Microsoft.AspNetCore.Razor.Language;
 
internal static partial class RazorCodeDocumentExtensions
{
    /// <summary>
    /// Adjusts the position if it's on a component end tag to use the corresponding start tag position.
    /// This ensures that hover, go to definition, and find all references work consistently for both
    /// start and end tags, since only start tags have source mappings.
    /// </summary>
    /// <param name="codeDocument">The code document.</param>
    /// <param name="hostDocumentIndex">The position in the host document.</param>
    /// <returns>
    /// The adjusted position if on a component end tag's name, otherwise the original position.
    /// </returns>
    public static int AdjustPositionForComponentEndTag(this RazorCodeDocument codeDocument, int hostDocumentIndex)
    {
        var root = codeDocument.GetRequiredSyntaxRoot();
        var owner = root.FindInnermostNode(hostDocumentIndex, includeWhitespace: false);
        if (owner is null)
        {
            return hostDocumentIndex;
        }
 
        // Check if we're on a component end tag and the position is within the tag name
        if (owner.FirstAncestorOrSelf<MarkupTagHelperEndTagSyntax>() is { } endTag &&
            endTag.Name.Span.IntersectsWith(hostDocumentIndex) &&
            endTag.GetStartTag() is MarkupTagHelperStartTagSyntax tagHelperStartTag)
        {
            // Calculate the offset within the end tag name
            var offsetInEndTag = hostDocumentIndex - endTag.Name.SpanStart;
 
            // Apply the same offset to the start tag name
            // This preserves the relative position within the tag name
            return tagHelperStartTag.Name.SpanStart + offsetInEndTag;
        }
 
        return hostDocumentIndex;
    }
}