Skip to content

Commit d269f6d

Browse files
committed
Revert revert of the component start tag mapping, but without line directives this time
1 parent b14ca48 commit d269f6d

File tree

17 files changed

+272
-65
lines changed

17 files changed

+272
-65
lines changed

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDesignTimeNodeWriter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -563,7 +563,7 @@ public override void WriteComponent(CodeRenderingContext context, ComponentInter
563563

564564
public override void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node)
565565
{
566-
base.WriteComponentTypeInferenceMethod(context, node, returnComponentType: true, allowNameof: false);
566+
base.WriteComponentTypeInferenceMethod(context, node, returnComponentType: true, allowNameof: false, mapComponentStartTag: false);
567567
}
568568

569569
private void WriteTypeInferenceMethodParameterInnards(CodeRenderingContext context, TypeInferenceMethodParameter parameter)

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentLoweringPass.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
using System;
77
using System.Collections.Generic;
8+
using System.Diagnostics;
89
using System.Linq;
910
using System.Threading;
1011
using Microsoft.AspNetCore.Razor.Language.Intermediate;
@@ -142,12 +143,14 @@ static TagHelperDescriptor GetTagHelperOrAddDiagnostic(TagHelperIntermediateNode
142143

143144
private static ComponentIntermediateNode RewriteAsComponent(TagHelperIntermediateNode node, TagHelperDescriptor tagHelper)
144145
{
146+
Debug.Assert(node.StartTagSpan.HasValue, "Component tags should always have a start tag span.");
145147
var component = new ComponentIntermediateNode()
146148
{
147149
Component = tagHelper,
148150
Source = node.Source,
149151
TagName = node.TagName,
150152
TypeName = tagHelper.TypeName,
153+
StartTagSpan = node.StartTagSpan.AssumeNotNull(),
151154
};
152155

153156
component.AddDiagnosticsFromNode(node);

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentNodeWriter.cs

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected bool ShouldSuppressTypeInferenceCall(ComponentIntermediateNode node)
7575
return node.Diagnostics.Any(d => d.Id == ComponentDiagnosticFactory.GenericComponentTypeInferenceUnderspecified.Id);
7676
}
7777

78-
protected void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node, bool returnComponentType, bool allowNameof)
78+
protected void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node, bool returnComponentType, bool allowNameof, bool mapComponentStartTag)
7979
{
8080
if (context == null)
8181
{
@@ -166,7 +166,18 @@ protected void WriteComponentTypeInferenceMethod(CodeRenderingContext context, C
166166
context.CodeWriter.Write(".");
167167
context.CodeWriter.Write(ComponentsApi.RenderTreeBuilder.OpenComponent);
168168
context.CodeWriter.Write("<");
169-
context.CodeWriter.Write(node.Component.TypeName);
169+
170+
if (mapComponentStartTag)
171+
{
172+
var nonGenericTypeName = TypeNameHelper.GetNonGenericTypeName(node.Component.TypeName, out var genericTypeParameterList);
173+
WriteComponentTypeName(context, node.Component, nonGenericTypeName);
174+
context.CodeWriter.Write(genericTypeParameterList);
175+
}
176+
else
177+
{
178+
context.CodeWriter.Write(node.Component.TypeName);
179+
}
180+
170181
context.CodeWriter.Write(">(");
171182
context.CodeWriter.Write("seq");
172183
context.CodeWriter.Write(");");
@@ -530,6 +541,40 @@ protected static void WriteGloballyQualifiedTypeName(CodeRenderingContext contex
530541
}
531542
}
532543

544+
protected static void WriteComponentTypeName(CodeRenderingContext context, ComponentIntermediateNode node, ReadOnlyMemory<char> nonGenericTypeName)
545+
{
546+
// The type name we are given may or may not be globally qualified, and we want to map it to the component start
547+
// tag, which may or may not be fully qualified. ie "global::My.Fun.Component" could map to just "Component"
548+
549+
// Write out "global::" if it's present, and trim it off
550+
var lastColon = nonGenericTypeName.Span.LastIndexOf(':');
551+
if (lastColon > -1)
552+
{
553+
lastColon++;
554+
context.CodeWriter.Write(nonGenericTypeName[0..lastColon]);
555+
nonGenericTypeName = nonGenericTypeName.Slice(lastColon);
556+
}
557+
558+
// If the start tag is shorter than the type name, then it must not be a fully qualified tag, so write out
559+
// the namespace parts and trim. Razor components don't support nested types, so this logic doesn't either.
560+
if (node.StartTagSpan.Length < nonGenericTypeName.Length)
561+
{
562+
var lastDot = nonGenericTypeName.Span.LastIndexOf('.');
563+
if (lastDot > -1)
564+
{
565+
lastDot++;
566+
context.CodeWriter.Write(nonGenericTypeName[0..lastDot]);
567+
nonGenericTypeName = nonGenericTypeName.Slice(lastDot);
568+
}
569+
}
570+
571+
var offset = nonGenericTypeName.Span.StartsWith('@')
572+
? 1
573+
: 0;
574+
context.AddSourceMappingFor(node.StartTagSpan, offset);
575+
context.CodeWriter.Write(nonGenericTypeName);
576+
}
577+
533578
[DebuggerDisplay($"{{{nameof(GetDebuggerDisplay)}(),nq}}")]
534579
protected internal readonly struct SeqName(int index) : IWriteableValue
535580
{

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRuntimeNodeWriter.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,10 @@ public override void WriteComponent(CodeRenderingContext context, ComponentInter
378378
context.CodeWriter.Write(ComponentsApi.RenderTreeBuilder.OpenComponent);
379379
context.CodeWriter.Write("<");
380380

381-
TypeNameHelper.WriteGloballyQualifiedName(context.CodeWriter, TypeNameHelper.GetNonGenericTypeName(node.TypeName));
381+
var nonGenericTypeName = TypeNameHelper.GetNonGenericTypeName(node.TypeName, out _);
382+
TypeNameHelper.WriteGlobalPrefixIfNeeded(context.CodeWriter, nonGenericTypeName);
383+
WriteComponentTypeName(context, node, nonGenericTypeName);
384+
382385
if (!node.OrderedTypeArguments.IsDefaultOrEmpty)
383386
{
384387
context.CodeWriter.Write("<");
@@ -540,7 +543,7 @@ public override void WriteComponent(CodeRenderingContext context, ComponentInter
540543

541544
public override void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node)
542545
{
543-
WriteComponentTypeInferenceMethod(context, node, returnComponentType: false, allowNameof: true);
546+
WriteComponentTypeInferenceMethod(context, node, returnComponentType: false, allowNameof: true, mapComponentStartTag: true);
544547
}
545548

546549
private void WriteTypeInferenceMethodParameterInnards(CodeRenderingContext context, TypeInferenceMethodParameter parameter)

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/TypeNameHelper.cs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,15 @@ public static void WriteGloballyQualifiedName(CodeWriter codeWriter, string type
7979
}
8080

8181
internal static void WriteGloballyQualifiedName(CodeWriter codeWriter, ReadOnlyMemory<char> typeName)
82+
{
83+
WriteGlobalPrefixIfNeeded(codeWriter, typeName);
84+
codeWriter.Write(typeName);
85+
}
86+
87+
/// <summary>
88+
/// Writes "global::" if the typename doesn't already start with it and isn't a predefined type.
89+
/// </summary>
90+
internal static void WriteGlobalPrefixIfNeeded(CodeWriter codeWriter, ReadOnlyMemory<char> typeName)
8291
{
8392
if (typeName.Length == 0)
8493
{
@@ -89,7 +98,6 @@ internal static void WriteGloballyQualifiedName(CodeWriter codeWriter, ReadOnlyM
8998

9099
if (typeNameSpan.StartsWith(GlobalPrefix.AsSpan(), StringComparison.Ordinal))
91100
{
92-
codeWriter.Write(typeName);
93101
return;
94102
}
95103

@@ -98,7 +106,6 @@ internal static void WriteGloballyQualifiedName(CodeWriter codeWriter, ReadOnlyM
98106
// just skip prefixing tuples.
99107
if (typeNameSpan[0] == '(')
100108
{
101-
codeWriter.Write(typeName);
102109
return;
103110
}
104111

@@ -107,24 +114,25 @@ internal static void WriteGloballyQualifiedName(CodeWriter codeWriter, ReadOnlyM
107114
if (typeNameSpan.Length < 3 || typeNameSpan.Length > 7)
108115
{
109116
codeWriter.Write(GlobalPrefix);
110-
codeWriter.Write(typeName);
111117
return;
112118
}
113119

114120
if (PredefinedTypeNames.Contains(typeName))
115121
{
116-
codeWriter.Write(typeName);
117122
return;
118123
}
119124

120125
codeWriter.Write(GlobalPrefix);
121-
codeWriter.Write(typeName);
122126
}
123127

124-
internal static ReadOnlyMemory<char> GetNonGenericTypeName(string typeName)
128+
internal static ReadOnlyMemory<char> GetNonGenericTypeName(string typeName, out ReadOnlyMemory<char> genericTypeParameterList)
125129
{
126130
var memory = typeName.AsMemory();
127131
var index = memory.Span.IndexOf('<');
132+
133+
genericTypeParameterList = index == -1
134+
? default
135+
: memory[index..];
128136
return index == -1 ? memory : memory[..index];
129137
}
130138
}

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/DefaultRazorIntermediateNodeLoweringPhase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1753,7 +1753,8 @@ public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax no
17531753
TagName = tagName,
17541754
TagMode = info.TagMode,
17551755
Source = BuildSourceSpanFromNode(node),
1756-
TagHelpers = info.BindingResult.Descriptors
1756+
TagHelpers = info.BindingResult.Descriptors,
1757+
StartTagSpan = node.StartTag.Name.GetSourceSpan(SourceDocument)
17571758
};
17581759

17591760
if (node.StartTag != null &&

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/ComponentIntermediateNode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public sealed class ComponentIntermediateNode : IntermediateNode
4949

5050
public string TypeName { get; set; }
5151

52+
public SourceSpan StartTagSpan { get; init; }
53+
5254
public override void Accept(IntermediateNodeVisitor visitor)
5355
{
5456
if (visitor == null)

src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Intermediate/TagHelperIntermediateNode.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ public sealed class TagHelperIntermediateNode : IntermediateNode
1111
public required TagMode TagMode { get; init; }
1212
public required string TagName { get; init; }
1313

14+
/// <summary>
15+
/// The source span of the start tag of the component that this tag helper represents, or null for an Mvc tag helper
16+
/// </summary>
17+
public SourceSpan? StartTagSpan { get; init; }
18+
1419
public ImmutableArray<TagHelperDescriptor> TagHelpers { get; init => field = value.NullToEmpty(); } = [];
1520

1621
public override IntermediateNodeCollection Children { get => field ??= []; }

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/Formatting/Passes/CSharpOnTypeFormattingPass.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,11 @@ private static bool ShouldFormat(FormattingContext context, TextSpan mappingSpan
900900
return false;
901901
}
902902

903+
if (IsComponentStartTagName())
904+
{
905+
return false;
906+
}
907+
903908
if (IsInHtmlAttributeValue())
904909
{
905910
return false;
@@ -1004,6 +1009,19 @@ bool IsInBoundComponentAttributeName()
10041009
} && !options.IsLineRequest;
10051010
}
10061011

1012+
bool IsComponentStartTagName()
1013+
{
1014+
// E.g, (| is position)
1015+
//
1016+
// `<|Component>` - true
1017+
//
1018+
// As above, we map component elements, so GTD and FAR works, there could be C# mapping for them.
1019+
// We don't want the mapping to make the formatting engine think it needs to apply C# indentation rules.
1020+
1021+
return owner is MarkupTagHelperStartTagSyntax startTag &&
1022+
startTag.Name.Span.Contains(mappingSpan.Start);
1023+
}
1024+
10071025
bool IsInHtmlAttributeValue()
10081026
{
10091027
// E.g, (| is position)

src/Razor/src/Microsoft.CodeAnalysis.Razor.Workspaces/GoToDefinition/AbstractDefinitionService.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using Microsoft.CodeAnalysis.Razor.DocumentMapping;
1010
using Microsoft.CodeAnalysis.Razor.Logging;
1111
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
12-
using Microsoft.CodeAnalysis.Razor.Protocol;
1312
using Microsoft.CodeAnalysis.Razor.Workspaces;
1413
using Microsoft.CodeAnalysis.Text;
1514
using CSharpSyntaxKind = Microsoft.CodeAnalysis.CSharp.SyntaxKind;
@@ -35,13 +34,6 @@ internal abstract class AbstractDefinitionService(
3534
bool includeMvcTagHelpers,
3635
CancellationToken cancellationToken)
3736
{
38-
39-
// If we're in C# then there is no point checking for a component tag, because there won't be one
40-
if (positionInfo.LanguageKind == RazorLanguageKind.CSharp)
41-
{
42-
return null;
43-
}
44-
4537
if (!includeMvcTagHelpers && !documentSnapshot.FileKind.IsComponent())
4638
{
4739
_logger.LogInformation($"'{documentSnapshot.FileKind}' is not a component type.");

0 commit comments

Comments
 (0)