From 8a054924076962c6d29188267bc7a00cc7d582ac Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 13 Mar 2024 15:27:46 +0100 Subject: [PATCH 01/11] Allow components in global namespace --- .../CSharp/BindTagHelperDescriptorProvider.cs | 3 +- .../ComponentTagHelperDescriptorProvider.cs | 9 ++- ...EventHandlerTagHelperDescriptorProvider.cs | 3 +- .../CodeGeneration/CodeWriterExtensions.cs | 32 +++++++-- .../ComponentDocumentClassifierPass.cs | 9 +-- .../ComponentRenderModeDirectivePass.cs | 3 +- .../Extensions/MetadataAttributePass.cs | 8 ++- .../Language/RazorCodeDocumentExtensions.cs | 30 ++++++--- .../ViewComponentTagHelperPass.cs | 3 +- .../AssemblyAttributeInjectionPass.cs | 8 ++- .../ViewComponentTagHelperPass.cs | 3 +- .../src/Mvc/ViewComponentTagHelperPass.cs | 3 +- .../RazorSourceGeneratorComponentTests.cs | 45 +++++++++++++ .../EmptyRootNamespace/Component2_razor.g.cs | 25 +++++++ .../EmptyRootNamespace/Component3_razor.g.cs | 27 ++++++++ .../Shared_Component1_razor.g.cs | 31 +++++++++ .../EmptyRootNamespace/Views_Home_Index.html | 6 ++ .../Views_Home_Index_cshtml.g.cs | 65 +++++++++++++++++++ 18 files changed, 282 insertions(+), 31 deletions(-) create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component2_razor.g.cs create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component3_razor.g.cs create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Shared_Component1_razor.g.cs create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index.html create mode 100644 src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs index f916ea1e0ab..d806c859e57 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/BindTagHelperDescriptorProvider.cs @@ -343,7 +343,8 @@ private ref struct DisplayNameHelper(INamedTypeSymbol type) private (string Type, string Namespace)? _names; public (string Type, string Namespace) GetNames() - => _names ??= (_type.ToDisplayString(), _type.ContainingNamespace.ToDisplayString()); + => _names ??= (_type.ToDisplayString(), + _type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat)); } private static TagHelperDescriptor CreateElementBindTagHelper( diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs index 561cbb6d55e..0f53faceb9f 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs @@ -115,8 +115,10 @@ private static TagHelperDescriptor CreateNameMatchingDescriptor( if (fullyQualified) { - var containingNamespace = type.ContainingNamespace.ToDisplayString(); - var fullName = $"{containingNamespace}.{type.Name}"; + var containingNamespace = type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat); + var fullName = string.IsNullOrEmpty(containingNamespace) + ? type.Name + : $"{containingNamespace}.{type.Name}"; builder.TagMatchingRule(r => { @@ -125,7 +127,8 @@ private static TagHelperDescriptor CreateNameMatchingDescriptor( metadata.Add(ComponentMetadata.Component.NameMatchKey, ComponentMetadata.Component.FullyQualifiedNameMatch); } - else + // If the component is in the global namespace, skip adding this rule which is the same as the fully qualified one. + else if (!type.ContainingNamespace.IsGlobalNamespace) { builder.TagMatchingRule(r => { diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs index dbd1c00447a..058f6485b3e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/EventHandlerTagHelperDescriptorProvider.cs @@ -101,7 +101,8 @@ private ref struct DisplayNameHelper(INamedTypeSymbol type) public (string Type, string Namespace) GetNames() { - _names ??= (_type.ToDisplayString(), _type.ContainingNamespace.ToDisplayString()); + _names ??= (_type.ToDisplayString(), + _type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat)); return _names.GetValueOrDefault(); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs index 4fbd31097e9..65bf9266eee 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/CodeGeneration/CodeWriterExtensions.cs @@ -392,12 +392,19 @@ private static CSharpCodeWritingScope BuildLambda(CodeWriter writer, bool async, return scope; } - public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, string name) +#nullable enable + public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, string? name) { + if (name.IsNullOrEmpty()) + { + return new CSharpCodeWritingScope(writer, writeBraces: false); + } + writer.Write("namespace ").WriteLine(name); return new CSharpCodeWritingScope(writer); } +#nullable disable public static CSharpCodeWritingScope BuildClassDeclaration( this CodeWriter writer, @@ -658,13 +665,15 @@ public struct CSharpCodeWritingScope : IDisposable { private readonly CodeWriter _writer; private readonly bool _autoSpace; + private readonly bool _writeBraces; private readonly int _tabSize; private int _startIndent; - public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace = true) + public CSharpCodeWritingScope(CodeWriter writer, bool autoSpace = true, bool writeBraces = true) { _writer = writer; _autoSpace = autoSpace; + _writeBraces = writeBraces; _tabSize = writer.TabSize; _startIndent = -1; // Set in WriteStartScope @@ -680,7 +689,15 @@ private void WriteStartScope() { TryAutoSpace(" "); - _writer.WriteLine("{"); + if (_writeBraces) + { + _writer.WriteLine("{"); + } + else + { + _writer.WriteLine(); + } + _writer.CurrentIndent += _tabSize; _startIndent = _writer.CurrentIndent; } @@ -695,7 +712,14 @@ private void WriteEndScope() _writer.CurrentIndent -= _tabSize; } - _writer.WriteLine("}"); + if (_writeBraces) + { + _writer.WriteLine("}"); + } + else + { + _writer.WriteLine(); + } } private void TryAutoSpace(string spaceCharacter) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs index 1cd3b31f79a..19607ae27f4 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs @@ -62,12 +62,13 @@ protected override void OnDocumentStructureCreated( ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method) { - if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var computedNamespace) || - !TryComputeClassName(codeDocument, out var computedClass)) + if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, allowEmptyRootNamespace: true, out var computedNamespace)) { - // If we can't compute a nice namespace (no relative path) then just generate something - // mangled. computedNamespace = FallbackRootNamespace; + } + + if (!TryComputeClassName(codeDocument, out var computedClass)) + { var checksum = ChecksumUtilities.BytesToString(codeDocument.Source.Text.GetChecksum()); computedClass = $"AspNetCore_{checksum}"; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs index f19128ad9a6..006f1889a9c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentRenderModeDirectivePass.cs @@ -89,10 +89,11 @@ child is not DirectiveTokenIntermediateNode directiveToken // generate the attribute usage on top of the class var attributeNode = new CSharpCodeIntermediateNode(); + var namespaceSeparator = string.IsNullOrEmpty(@namespace.Content) ? string.Empty : "."; attributeNode.Children.Add(new IntermediateToken() { Kind = TokenKind.CSharp, - Content = $"[global::{@namespace.Content}.{@class.ClassName}.{GeneratedRenderModeAttributeName}]", + Content = $"[global::{@namespace.Content}{namespaceSeparator}{@class.ClassName}.{GeneratedRenderModeAttributeName}]", }); // Insert the new attribute on top of the class diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributePass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributePass.cs index f9384ead685..3a94798f05e 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributePass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Extensions/MetadataAttributePass.cs @@ -41,9 +41,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte // // If we can't use [RazorCompiledItem] then we don't care about the rest of the attributes. var @namespace = documentNode.FindPrimaryNamespace(); - if (@namespace == null || string.IsNullOrEmpty(@namespace.Content)) + if (@namespace == null) { - // No namespace node or it's incomplete. Skip. + // No namespace node. Skip. return; } @@ -70,7 +70,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte // [RazorCompiledItem] is an [assembly: ... ] attribute, so it needs to be applied at the global scope. documentNode.Children.Insert(0, new RazorCompiledItemAttributeIntermediateNode() { - TypeName = @namespace.Content + "." + @class.ClassName, + TypeName = string.IsNullOrEmpty(@namespace.Content) + ? @class.ClassName + : @namespace.Content + "." + @class.ClassName, Kind = documentNode.DocumentKind, Identifier = identifier, }); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs index ae0c3aa645b..396d2d89b07 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs @@ -303,12 +303,15 @@ public static bool TryComputeClassName(this RazorCodeDocument codeDocument, [Not } #nullable disable + public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace) + => TryComputeNamespace(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: false, out @namespace); + // In general documents will have a relative path (relative to the project root). // We can only really compute a nice namespace when we know a relative path. // // However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't // set up correctly. - public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace) + internal static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, bool allowEmptyRootNamespace, out string @namespace) { if (document == null) { @@ -318,7 +321,7 @@ public static bool TryComputeNamespace(this RazorCodeDocument document, bool fal @namespace = (string)document.Items[NamespaceKey]; if (@namespace is null) { - var result = TryComputeNamespaceCore(document, fallbackToRootNamespace, out @namespace); + var result = TryComputeNamespaceCore(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: allowEmptyRootNamespace, out @namespace); document.Items[NamespaceKey] = @namespace; return result; } @@ -326,14 +329,14 @@ public static bool TryComputeNamespace(this RazorCodeDocument document, bool fal #if DEBUG // In debug mode, even if we're cached, lets take the hit to run this again and make sure the cached value is correct. // This is to help us find issues with caching logic during development. - var validateResult = TryComputeNamespaceCore(document, fallbackToRootNamespace, out var validateNamespace); + var validateResult = TryComputeNamespaceCore(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: allowEmptyRootNamespace, out var validateNamespace); Debug.Assert(validateResult, "We couldn't compute the namespace, but have a cached value, so something has gone wrong"); Debug.Assert(validateNamespace == @namespace, $"We cached a namespace of {@namespace} but calculated that it should be {validateNamespace}"); #endif return true; - bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace) + bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootNamespace, bool allowEmptyRootNamespace, out string @namespace) { var filePath = document.Source.FilePath; if (filePath == null || document.Source.RelativePath == null || filePath.Length < document.Source.RelativePath.Length) @@ -397,11 +400,18 @@ bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootName var options = document.GetCodeGenerationOptions() ?? document.GetDocumentIntermediateNode()?.Options; baseNamespace = options?.RootNamespace; appendSuffix = true; - } - if (string.IsNullOrEmpty(baseNamespace)) + // null means RootNamespace wasn't set yet, so we fail for now, tooling seems to rely on this + if (baseNamespace is null || + (!allowEmptyRootNamespace && string.IsNullOrEmpty(baseNamespace))) + { + @namespace = null; + return false; + } + } + else { - // There was no valid @namespace directive and we couldn't compute the RootNamespace. + // There was no valid @namespace directive. @namespace = null; return false; } @@ -444,7 +454,11 @@ bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootName previousLength = builder.Length; - builder.Append('.'); + if (previousLength != 0) + { + builder.Append('.'); + } + CSharpIdentifier.AppendSanitized(builder, token); } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperPass.cs index fdeb8db13dc..da44cacbf7c 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version1_X/ViewComponentTagHelperPass.cs @@ -173,7 +173,8 @@ public bool Add(TagHelperDescriptor tagHelper) } var className = $"__Generated__{tagHelper.GetViewComponentName()}ViewComponentTagHelper"; - var fullyQualifiedName = $"{Namespace.Content}.{Class.ClassName}.{className}"; + var namespaceSeparator = string.IsNullOrEmpty(Namespace.Content) ? string.Empty : "."; + var fullyQualifiedName = $"{Namespace.Content}{namespaceSeparator}{Class.ClassName}.{className}"; var fieldName = GenerateFieldName(tagHelper); _tagHelpers.Add(tagHelper, (className, fullyQualifiedName, fieldName)); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/AssemblyAttributeInjectionPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/AssemblyAttributeInjectionPass.cs index 49262fca725..42e9c5532ea 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/AssemblyAttributeInjectionPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/AssemblyAttributeInjectionPass.cs @@ -23,9 +23,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte } var @namespace = documentNode.FindPrimaryNamespace(); - if (@namespace == null || string.IsNullOrEmpty(@namespace.Content)) + if (@namespace == null) { - // No namespace node or it's incomplete. Skip. + // No namespace node. Skip. return; } @@ -36,7 +36,9 @@ protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentInte return; } - var generatedTypeName = $"{@namespace.Content}.{@class.ClassName}"; + var generatedTypeName = string.IsNullOrEmpty(@namespace.Content) + ? @class.ClassName + : $"{@namespace.Content}.{@class.ClassName}"; // The MVC attributes require a relative path to be specified so that we can make a view engine path. // We can't use a rooted path because we don't know what the project root is. diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperPass.cs index 10eb289f0a5..cbacadea315 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc.Version2_X/ViewComponentTagHelperPass.cs @@ -173,7 +173,8 @@ public bool Add(TagHelperDescriptor tagHelper) } var className = $"__Generated__{tagHelper.GetViewComponentName()}ViewComponentTagHelper"; - var fullyQualifiedName = $"{Namespace.Content}.{Class.ClassName}.{className}"; + var namespaceSeparator = string.IsNullOrEmpty(Namespace.Content) ? string.Empty : "."; + var fullyQualifiedName = $"{Namespace.Content}{namespaceSeparator}{Class.ClassName}.{className}"; var fieldName = GenerateFieldName(tagHelper); _tagHelpers.Add(tagHelper, (className, fullyQualifiedName, fieldName)); diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs index 2b3bba4acfe..4e06db45ae6 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Mvc/ViewComponentTagHelperPass.cs @@ -180,7 +180,8 @@ public bool Add(TagHelperDescriptor tagHelper) } var className = $"__Generated__{tagHelper.GetViewComponentName()}ViewComponentTagHelper"; - var fullyQualifiedName = $"{Namespace.Content}.{Class.ClassName}.{className}"; + var namespaceSeparator = string.IsNullOrEmpty(Namespace.Content) ? string.Empty : "."; + var fullyQualifiedName = $"{Namespace.Content}{namespaceSeparator}{Class.ClassName}.{className}"; var fieldName = GenerateFieldName(tagHelper); _tagHelpers.Add(tagHelper, (className, fullyQualifiedName, fieldName)); diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs index 3d5d1691c3e..e52287dab7b 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/RazorSourceGeneratorComponentTests.cs @@ -143,6 +143,51 @@ Hello from Derived await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); } + [Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1954771")] + public async Task EmptyRootNamespace() + { + // Arrange + var project = CreateTestProject(new() + { + ["Views/Home/Index.cshtml"] = """ + @(await Html.RenderComponentAsync(RenderMode.Static)) + @(await Html.RenderComponentAsync(RenderMode.Static)) + """, + ["Shared/Component1.razor"] = """ + Component1 in Shared namespace + + + """, + ["Component2.razor"] = """ + Component2 in global namespace + """, + ["Component3.razor"] = """ + Component3 in global namespace + + """ + }, new() + { + ["Component4.cs"] = """ + using Microsoft.AspNetCore.Components; + public class Component4 : ComponentBase { } + """, + }); + var compilation = await project.GetCompilationAsync(); + var driver = await GetDriverAsync(project, options => + { + options.TestGlobalOptions["build_property.RootNamespace"] = string.Empty; + }); + + // Act + var result = RunGenerator(compilation!, ref driver, out compilation); + + // Assert + Assert.Empty(result.Diagnostics); + Assert.Equal(4, result.GeneratedSources.Length); + result.VerifyOutputsMatchBaseline(); + await VerifyRazorPageMatchesBaselineAsync(compilation, "Views_Home_Index"); + } + [Theory, CombinatorialData] public async Task AddComponentParameter( [CombinatorialValues("7.0", "8.0", "Latest")] string langVersion) diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component2_razor.g.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component2_razor.g.cs new file mode 100644 index 00000000000..886009739d0 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component2_razor.g.cs @@ -0,0 +1,25 @@ +#pragma checksum "Component2.razor" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "64a0fe8849732339e8ebc4698ec7367617b7b588" +// +#pragma warning disable 1591 + + #line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; + #line default + #line hidden + #nullable restore + public partial class Component2 : global::Microsoft.AspNetCore.Components.ComponentBase + #nullable disable + { + #pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.AddContent(0, "Component2 in global namespace"); + } + #pragma warning restore 1998 + } + +#pragma warning restore 1591 diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component3_razor.g.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component3_razor.g.cs new file mode 100644 index 00000000000..86097fd0f14 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Component3_razor.g.cs @@ -0,0 +1,27 @@ +#pragma checksum "Component3.razor" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "4319ac545b421bebcac37ee05f6e0c730be31183" +// +#pragma warning disable 1591 + + #line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; + #line default + #line hidden + #nullable restore + public partial class Component3 : global::Microsoft.AspNetCore.Components.ComponentBase + #nullable disable + { + #pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.AddMarkupContent(0, "Component3 in global namespace\r\n"); + __builder.OpenComponent(1); + __builder.CloseComponent(); + } + #pragma warning restore 1998 + } + +#pragma warning restore 1591 diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Shared_Component1_razor.g.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Shared_Component1_razor.g.cs new file mode 100644 index 00000000000..50c24cb9cf4 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Shared_Component1_razor.g.cs @@ -0,0 +1,31 @@ +#pragma checksum "Shared/Component1.razor" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "5d435cdce1d8978f1f0ffc5697761e2795ae0c94" +// +#pragma warning disable 1591 +namespace Shared +{ + #line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; + #line default + #line hidden + #nullable restore + public partial class Component1 : global::Microsoft.AspNetCore.Components.ComponentBase + #nullable disable + { + #pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.AddMarkupContent(0, "Component1 in Shared namespace\r\n"); + __builder.OpenComponent(1); + __builder.CloseComponent(); + __builder.AddMarkupContent(2, "\r\n"); + __builder.OpenComponent(3); + __builder.CloseComponent(); + } + #pragma warning restore 1998 + } +} +#pragma warning restore 1591 diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index.html b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index.html new file mode 100644 index 00000000000..6a743889b83 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index.html @@ -0,0 +1,6 @@ +Component1 in Shared namespace +Component2 in global namespace + +Component3 in global namespace +Component1 in Shared namespace +Component2 in global namespace diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs new file mode 100644 index 00000000000..89b369f3cb2 --- /dev/null +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs @@ -0,0 +1,65 @@ +#pragma checksum "Views/Home/Index.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "21502796fc325aa643d43afcd2ba3c6ad52ad693" +// +#pragma warning disable 1591 +[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCoreGeneratedDocument.Views_Home_Index), @"mvc.1.0.view", @"/Views/Home/Index.cshtml")] +namespace AspNetCoreGeneratedDocument +{ + #line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Mvc; + using global::Microsoft.AspNetCore.Mvc.Rendering; + using global::Microsoft.AspNetCore.Mvc.ViewFeatures; + #line default + #line hidden + [global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/Views/Home/Index.cshtml")] + [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute] + #nullable restore + internal sealed class Views_Home_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage + #nullable disable + { + #pragma warning disable 1998 + public async override global::System.Threading.Tasks.Task ExecuteAsync() + { +#nullable restore +#line (1,3)-(1,72) 6 "Views/Home/Index.cshtml" +Write(await Html.RenderComponentAsync(RenderMode.Static)); + +#line default +#line hidden +#nullable disable + WriteLiteral("\r\n"); +#nullable restore +#line (2,3)-(2,65) 6 "Views/Home/Index.cshtml" +Write(await Html.RenderComponentAsync(RenderMode.Static)); + +#line default +#line hidden +#nullable disable + } + #pragma warning restore 1998 + #nullable restore + [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] + public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; } = default!; + #nullable disable + #nullable restore + [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] + public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; } = default!; + #nullable disable + #nullable restore + [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] + public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; } = default!; + #nullable disable + #nullable restore + [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] + public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; } = default!; + #nullable disable + #nullable restore + [global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute] + public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper Html { get; private set; } = default!; + #nullable disable + } +} +#pragma warning restore 1591 From 9de3c1751e33a86c142fafc812966dfa1d6b93ed Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 13 Mar 2024 16:13:28 +0100 Subject: [PATCH 02/11] Update existing tests --- .../BasicComponent_DesignTime.codegen.cs | 2 +- .../BasicComponent_DesignTime.ir.txt | 2 +- .../BasicComponent_DesignTime.mappings.txt | 8 ++++---- .../BasicComponent_Runtime.codegen.cs | 2 +- .../BasicComponent_Runtime.ir.txt | 2 +- .../test/Extensions/MetadataAttributePassTest.cs | 15 ++++++++++++++- .../Semantic/SemanticTokensTest.cs | 2 +- 7 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs index 9b650e45a03..3c0217594f1 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs @@ -11,7 +11,7 @@ namespace __GeneratedComponent #line default #line hidden #nullable restore - public partial class AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable + public partial class BasicComponent : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable #nullable disable { #pragma warning disable 219 diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt index 5db67ba568f..175ad5e9ef0 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt @@ -5,7 +5,7 @@ UsingDirective - (67:2,1 [25] ) - global::System.Linq UsingDirective - (95:3,1 [36] ) - global::System.Threading.Tasks UsingDirective - (134:4,1 [45] ) - global::Microsoft.AspNetCore.Components - ClassDeclaration - - public partial - AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable + ClassDeclaration - - public partial - BasicComponent - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable DesignTimeDirective - DirectiveToken - (12:0,12 [11] BasicComponent.cshtml) - IDisposable CSharpCode - diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt index 87aaee7e725..89528c26f47 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt @@ -1,23 +1,23 @@ Source Location: (12:0,12 [11] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) |IDisposable| -Generated Location: (813:21,0 [11] ) +Generated Location: (752:21,0 [11] ) |IDisposable| Source Location: (38:1,13 [15] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) |this.ToString()| -Generated Location: (1413:39,13 [15] ) +Generated Location: (1352:39,13 [15] ) |this.ToString()| Source Location: (79:3,5 [29] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) |string.Format("{0}", "Hello")| -Generated Location: (1610:47,6 [29] ) +Generated Location: (1549:47,6 [29] ) |string.Format("{0}", "Hello")| Source Location: (132:6,12 [37] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) | void IDisposable.Dispose(){ } | -Generated Location: (1862:56,12 [37] ) +Generated Location: (1801:56,12 [37] ) | void IDisposable.Dispose(){ } | diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs index 3632868a85d..31290c3e8a2 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs @@ -12,7 +12,7 @@ namespace __GeneratedComponent #line default #line hidden #nullable restore - public partial class AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable + public partial class BasicComponent : global::Microsoft.AspNetCore.Components.ComponentBase, IDisposable #nullable disable { #pragma warning disable 1998 diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt index b6e6405ae15..4b535da09e5 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt @@ -5,7 +5,7 @@ UsingDirective - (67:2,1 [27] ) - global::System.Linq UsingDirective - (95:3,1 [38] ) - global::System.Threading.Tasks UsingDirective - (134:4,1 [45] ) - global::Microsoft.AspNetCore.Components - ClassDeclaration - - public partial - AspNetCore_3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable + ClassDeclaration - - public partial - BasicComponent - global::Microsoft.AspNetCore.Components.ComponentBase - IDisposable MethodDeclaration - - protected override - void - BuildRenderTree MarkupElement - (25:1,0 [91] BasicComponent.cshtml) - div HtmlAttribute - (29:1,4 [25] BasicComponent.cshtml) - class=" - " diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs index 2f09ba6967c..7891fc15c81 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/Extensions/MetadataAttributePassTest.cs @@ -133,7 +133,20 @@ public void Execute_NoNamespaceSet_Noops() pass.Execute(codeDocument, irDocument); // Assert - SingleChild(irDocument); + Assert.Equal(2, irDocument.Children.Count); + + var item = Assert.IsType(irDocument.Children[0]); + Assert.Equal("/test.cshtml", item.Identifier); + Assert.Equal("test", item.Kind); + Assert.Equal("Test", item.TypeName); + + Assert.Equal(2, @namespace.Children.Count); + var checksum = Assert.IsType(@namespace.Children[0]); + Assert.Equal(CodeAnalysis.Text.SourceHashAlgorithm.Sha256, checksum.ChecksumAlgorithm); + Assert.Equal("/test.cshtml", checksum.Identifier); + + var foundClass = Assert.IsType(@namespace.Children[1]); + Assert.Equal("Test", foundClass.ClassName); } [Fact] diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs index 1990c760e04..ced9191d2f6 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Semantic/SemanticTokensTest.cs @@ -888,7 +888,7 @@ public void GetMappedCSharpRanges_MinimalRangeVsSmallDisjointRanges_DisjointRang else { // Note that the expected lengths are different on Windows vs. Unix. - var expectedCsharpRangeLength = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1016 : 982; + var expectedCsharpRangeLength = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 945 : 911; Assert.True(codeDocument.TryGetMinimalCSharpRange(razorRange, out var csharpRange)); var textSpan = csharpRange.ToTextSpan(csharpSourceText); Assert.Equal(expectedCsharpRangeLength, textSpan.Length); From acf120886310f7b1310cf45e11c21228ca0be992 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 19 Mar 2024 15:27:51 +0100 Subject: [PATCH 03/11] Fixup baseline --- .../EmptyRootNamespace/Views_Home_Index_cshtml.g.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs index 89b369f3cb2..d9e020b6a81 100644 --- a/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs +++ b/src/Compiler/test/Microsoft.NET.Sdk.Razor.SourceGenerators.Tests/TestFiles/RazorSourceGeneratorComponentTests/EmptyRootNamespace/Views_Home_Index_cshtml.g.cs @@ -23,21 +23,25 @@ internal sealed class Views_Home_Index : global::Microsoft.AspNetCore.Mvc.Razor. #pragma warning disable 1998 public async override global::System.Threading.Tasks.Task ExecuteAsync() { + Write( #nullable restore -#line (1,3)-(1,72) 6 "Views/Home/Index.cshtml" -Write(await Html.RenderComponentAsync(RenderMode.Static)); +#line (1,3)-(1,72) "Views/Home/Index.cshtml" +await Html.RenderComponentAsync(RenderMode.Static) #line default #line hidden #nullable disable + ); WriteLiteral("\r\n"); + Write( #nullable restore -#line (2,3)-(2,65) 6 "Views/Home/Index.cshtml" -Write(await Html.RenderComponentAsync(RenderMode.Static)); +#line (2,3)-(2,65) "Views/Home/Index.cshtml" +await Html.RenderComponentAsync(RenderMode.Static) #line default #line hidden #nullable disable + ); } #pragma warning restore 1998 #nullable restore From a7713a7f445c6d96110e91476b0f8f97e640a6de Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 19 Mar 2024 15:59:24 +0100 Subject: [PATCH 04/11] Test formatting of component in global namespace --- .../Formatting_NetFx/FormattingTestBase.cs | 14 +++++++++----- .../Formatting_NetFx/RazorFormattingTest.cs | 7 ++++--- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs index e1d0b81376b..276b9472349 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -44,7 +45,8 @@ private protected async Task RunFormattingTestAsync( string? fileKind = null, ImmutableArray tagHelpers = default, bool allowDiagnostics = false, - RazorLSPOptions? razorLSPOptions = null) + RazorLSPOptions? razorLSPOptions = null, + bool inGlobalNamespace = false) { // Arrange fileKind ??= FileKinds.Component; @@ -59,7 +61,7 @@ private protected async Task RunFormattingTestAsync( var path = "file:///path/to/Document." + fileKind; var uri = new Uri(path); - var (codeDocument, documentSnapshot) = CreateCodeDocumentAndSnapshot(source, uri.AbsolutePath, tagHelpers, fileKind, allowDiagnostics); + var (codeDocument, documentSnapshot) = CreateCodeDocumentAndSnapshot(source, uri.AbsolutePath, tagHelpers, fileKind, allowDiagnostics, inGlobalNamespace); var options = new FormattingOptions() { TabSize = tabSize, @@ -218,7 +220,7 @@ private static SourceText ApplyEdits(SourceText source, TextEdit[] edits) return source.WithChanges(changes); } - private static (RazorCodeDocument, IDocumentSnapshot) CreateCodeDocumentAndSnapshot(SourceText text, string path, ImmutableArray tagHelpers = default, string? fileKind = default, bool allowDiagnostics = false) + private static (RazorCodeDocument, IDocumentSnapshot) CreateCodeDocumentAndSnapshot(SourceText text, string path, ImmutableArray tagHelpers = default, string? fileKind = default, bool allowDiagnostics = false, bool inGlobalNamespace = false) { fileKind ??= FileKinds.Component; tagHelpers = tagHelpers.NullToEmpty(); @@ -228,7 +230,9 @@ private static (RazorCodeDocument, IDocumentSnapshot) CreateCodeDocumentAndSnaps tagHelpers = tagHelpers.AddRange(RazorTestResources.BlazorServerAppTagHelpers); } - var sourceDocument = RazorSourceDocument.Create(text, RazorSourceDocumentProperties.Create(path, path)); + var sourceDocument = RazorSourceDocument.Create(text, RazorSourceDocumentProperties.Create( + filePath: path, + relativePath: inGlobalNamespace ? Path.GetFileName(path) : path)); const string DefaultImports = """ @using BlazorApp1 @@ -256,7 +260,7 @@ @using Microsoft.AspNetCore.Components.Web var projectEngine = RazorProjectEngine.Create(builder => { - builder.SetRootNamespace("Test"); + builder.SetRootNamespace(inGlobalNamespace ? string.Empty : "Test"); builder.Features.Add(new DefaultTypeNameFeature()); RazorExtensions.Register(builder); }); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs index b2cbdd1c2b1..a9e5754d120 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs @@ -77,8 +77,8 @@ private void IncrementCount() """); } - [Fact] - public async Task CodeBlock_IndentedBlock_MaintainsIndent() + [Theory, CombinatorialData] + public async Task CodeBlock_IndentedBlock_MaintainsIndent(bool inGlobalNamespace) { await RunFormattingTestAsync( input: """ @@ -106,7 +106,8 @@ private void IncrementCount() } } - """); + """, + inGlobalNamespace: inGlobalNamespace); } [Fact] From e9f30fbb3436cd7c9e0f0d8d1494f2c1510d6e4a Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Mar 2024 12:06:34 +0100 Subject: [PATCH 05/11] Add design-time codegen test --- .../ComponentCodeGenerationTestBase.cs | 33 ++++++++++++ .../TestComponent.codegen.cs | 50 +++++++++++++++++++ .../EmptyRootNamespace/TestComponent.ir.txt | 25 ++++++++++ .../TestComponent.codegen.cs | 29 +++++++++++ .../EmptyRootNamespace/TestComponent.ir.txt | 14 ++++++ .../RazorIntegrationTestBase.cs | 2 +- 6 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs create mode 100644 src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt create mode 100644 src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs create mode 100644 src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs index 953a7b47924..aa3b5d50ae2 100644 --- a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/IntegrationTests/ComponentCodeGenerationTestBase.cs @@ -9633,6 +9633,39 @@ @preservewhitespace false #endregion + #region Namespace + + [IntegrationTestFact] + public void EmptyRootNamespace() + { + DefaultRootNamespace = string.Empty; + + AdditionalSyntaxTrees.Add(Parse(""" + using Microsoft.AspNetCore.Components; + public class Component1 : ComponentBase { } + namespace Shared + { + public class Component2 : ComponentBase { } + } + class C + { + void M1(TestComponent t) { } + void M2(global::TestComponent t) { } + } + """)); + var generated = CompileToCSharp(""" +

Generated

+ + + """); + + AssertDocumentNodeMatchesBaseline(generated.CodeDocument); + AssertCSharpDocumentMatchesBaseline(generated.CodeDocument); + CompileToAssembly(generated); + } + + #endregion + #region "CSS scoping" [IntegrationTestFact] public void Component_WithCssScope() diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs new file mode 100644 index 00000000000..3f92a5da05f --- /dev/null +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs @@ -0,0 +1,50 @@ +// +#pragma warning disable 1591 + + #line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; + #line default + #line hidden + #nullable restore + public partial class TestComponent : global::Microsoft.AspNetCore.Components.ComponentBase + #nullable disable + { + #pragma warning disable 219 + private void __RazorDirectiveTokenHelpers__() { + } + #pragma warning restore 219 + #pragma warning disable 0414 + private static object __o = null; + #pragma warning restore 0414 + #pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.AddAttribute(-1, "ChildContent", (global::Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + } + )); +#nullable restore +#line 2 "x:\dir\subdir\Test\TestComponent.cshtml" +__o = typeof(global::Component1); + +#line default +#line hidden +#nullable disable + __builder.AddAttribute(-1, "ChildContent", (global::Microsoft.AspNetCore.Components.RenderFragment)((__builder2) => { + } + )); +#nullable restore +#line 3 "x:\dir\subdir\Test\TestComponent.cshtml" +__o = typeof(global::Shared.Component2); + +#line default +#line hidden +#nullable disable + } + #pragma warning restore 1998 + } + +#pragma warning restore 1591 diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt new file mode 100644 index 00000000000..1b0c909f635 --- /dev/null +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentDesignTimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt @@ -0,0 +1,25 @@ +Document - + NamespaceDeclaration - - + UsingDirective - (3:1,1 [20] ) - global::System + UsingDirective - (26:2,1 [40] ) - global::System.Collections.Generic + UsingDirective - (69:3,1 [25] ) - global::System.Linq + UsingDirective - (97:4,1 [36] ) - global::System.Threading.Tasks + UsingDirective - (136:5,1 [45] ) - global::Microsoft.AspNetCore.Components + ClassDeclaration - - public partial - TestComponent - global::Microsoft.AspNetCore.Components.ComponentBase - + DesignTimeDirective - + CSharpCode - + IntermediateToken - - CSharp - #pragma warning disable 0414 + CSharpCode - + IntermediateToken - - CSharp - private static object __o = null; + CSharpCode - + IntermediateToken - - CSharp - #pragma warning restore 0414 + MethodDeclaration - - protected override - void - BuildRenderTree + MarkupElement - (0:0,0 [18] x:\dir\subdir\Test\TestComponent.cshtml) - h1 + HtmlContent - (4:0,4 [9] x:\dir\subdir\Test\TestComponent.cshtml) + LazyIntermediateToken - (4:0,4 [9] x:\dir\subdir\Test\TestComponent.cshtml) - Html - Generated + HtmlContent - (18:0,18 [2] x:\dir\subdir\Test\TestComponent.cshtml) + LazyIntermediateToken - (18:0,18 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n + Component - (20:1,0 [14] x:\dir\subdir\Test\TestComponent.cshtml) - Component1 + HtmlContent - (34:1,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) + LazyIntermediateToken - (34:1,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n + Component - (36:2,0 [21] x:\dir\subdir\Test\TestComponent.cshtml) - Shared.Component2 diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs new file mode 100644 index 00000000000..f5b2f59cde3 --- /dev/null +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.codegen.cs @@ -0,0 +1,29 @@ +// +#pragma warning disable 1591 + + #line default + using global::System; + using global::System.Collections.Generic; + using global::System.Linq; + using global::System.Threading.Tasks; + using global::Microsoft.AspNetCore.Components; + #line default + #line hidden + #nullable restore + public partial class TestComponent : global::Microsoft.AspNetCore.Components.ComponentBase + #nullable disable + { + #pragma warning disable 1998 + protected override void BuildRenderTree(global::Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder __builder) + { + __builder.AddMarkupContent(0, "

Generated

\r\n"); + __builder.OpenComponent(1); + __builder.CloseComponent(); + __builder.AddMarkupContent(2, "\r\n"); + __builder.OpenComponent(3); + __builder.CloseComponent(); + } + #pragma warning restore 1998 + } + +#pragma warning restore 1591 diff --git a/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt new file mode 100644 index 00000000000..4a3605d3f4d --- /dev/null +++ b/src/Compiler/Microsoft.AspNetCore.Razor.Language/test/TestFiles/IntegrationTests/ComponentRuntimeCodeGenerationTest/EmptyRootNamespace/TestComponent.ir.txt @@ -0,0 +1,14 @@ +Document - + NamespaceDeclaration - - + UsingDirective - (3:1,1 [22] ) - global::System + UsingDirective - (26:2,1 [42] ) - global::System.Collections.Generic + UsingDirective - (69:3,1 [27] ) - global::System.Linq + UsingDirective - (97:4,1 [38] ) - global::System.Threading.Tasks + UsingDirective - (136:5,1 [47] ) - global::Microsoft.AspNetCore.Components + ClassDeclaration - - public partial - TestComponent - global::Microsoft.AspNetCore.Components.ComponentBase - + MethodDeclaration - - protected override - void - BuildRenderTree + MarkupBlock - -

Generated

\n + Component - (20:1,0 [14] x:\dir\subdir\Test\TestComponent.cshtml) - Component1 + HtmlContent - (34:1,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) + LazyIntermediateToken - (34:1,14 [2] x:\dir\subdir\Test\TestComponent.cshtml) - Html - \n + Component - (36:2,0 [21] x:\dir\subdir\Test\TestComponent.cshtml) - Shared.Component2 diff --git a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorIntegrationTestBase.cs b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorIntegrationTestBase.cs index 3adedb73fb9..d0aa246c2a5 100644 --- a/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorIntegrationTestBase.cs +++ b/src/Shared/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorIntegrationTestBase.cs @@ -84,7 +84,7 @@ public RazorIntegrationTestBase() internal virtual RazorConfiguration Configuration { get; } - internal virtual string DefaultRootNamespace { get; } + internal string DefaultRootNamespace { get; set; } internal virtual string DefaultFileName { get; } From 61ead073f8702ca5b8e34aa91d329b3d6ad6cfaf Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Mar 2024 12:26:38 +0100 Subject: [PATCH 06/11] Automatically determine where empty root namespace is allowed --- .../BasicComponent_DesignTime.codegen.cs | 2 +- .../BasicComponent_DesignTime.ir.txt | 2 +- .../BasicComponent_DesignTime.mappings.txt | 8 ++++---- .../BasicComponent_Runtime.codegen.cs | 2 +- .../BasicComponent_Runtime.ir.txt | 2 +- .../Components/ComponentDocumentClassifierPass.cs | 2 +- .../src/Language/RazorCodeDocumentExtensions.cs | 15 +++++++-------- 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs index 3c0217594f1..fcc8df2a724 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs @@ -1,6 +1,6 @@ // #pragma warning disable 1591 -namespace __GeneratedComponent +namespace TestFiles.IntegrationTests.CodeGenerationIntegrationTest { #line default using global::System; diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt index 175ad5e9ef0..6b8b55abf56 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt @@ -1,5 +1,5 @@ Document - - NamespaceDeclaration - - __GeneratedComponent + NamespaceDeclaration - - TestFiles.IntegrationTests.CodeGenerationIntegrationTest UsingDirective - (1:0,1 [20] ) - global::System UsingDirective - (24:1,1 [40] ) - global::System.Collections.Generic UsingDirective - (67:2,1 [25] ) - global::System.Linq diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt index 89528c26f47..9981d0caf97 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt @@ -1,23 +1,23 @@ Source Location: (12:0,12 [11] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) |IDisposable| -Generated Location: (752:21,0 [11] ) +Generated Location: (788:21,0 [11] ) |IDisposable| Source Location: (38:1,13 [15] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) |this.ToString()| -Generated Location: (1352:39,13 [15] ) +Generated Location: (1388:39,13 [15] ) |this.ToString()| Source Location: (79:3,5 [29] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) |string.Format("{0}", "Hello")| -Generated Location: (1549:47,6 [29] ) +Generated Location: (1585:47,6 [29] ) |string.Format("{0}", "Hello")| Source Location: (132:6,12 [37] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml) | void IDisposable.Dispose(){ } | -Generated Location: (1801:56,12 [37] ) +Generated Location: (1837:56,12 [37] ) | void IDisposable.Dispose(){ } | diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs index 1d1fed5ccf0..8becffde010 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs @@ -1,7 +1,7 @@ #pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "3708c9fcd2e1ecb6cbaba92bcc60927f830690f42092c4aa1a7275c2f37020fd" // #pragma warning disable 1591 -namespace __GeneratedComponent +namespace TestFiles.IntegrationTests.CodeGenerationIntegrationTest { #line default using global::System; diff --git a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt index 4b535da09e5..970d4c4e3c6 100644 --- a/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt +++ b/src/Compiler/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt @@ -1,5 +1,5 @@ Document - - NamespaceDeclaration - - __GeneratedComponent + NamespaceDeclaration - - TestFiles.IntegrationTests.CodeGenerationIntegrationTest UsingDirective - (1:0,1 [22] ) - global::System UsingDirective - (24:1,1 [42] ) - global::System.Collections.Generic UsingDirective - (67:2,1 [27] ) - global::System.Linq diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs index 13f258777f1..407cbf24cba 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/Components/ComponentDocumentClassifierPass.cs @@ -62,7 +62,7 @@ protected override void OnDocumentStructureCreated( ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method) { - if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, allowEmptyRootNamespace: true, out var computedNamespace, out var computedNamespaceSpan)) + if (!codeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var computedNamespace, out var computedNamespaceSpan)) { computedNamespace = FallbackRootNamespace; } diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs index 43501a2ad78..2757ca9208f 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/Language/RazorCodeDocumentExtensions.cs @@ -304,14 +304,14 @@ public static bool TryComputeClassName(this RazorCodeDocument codeDocument, [Not #nullable disable public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace) - => TryComputeNamespace(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: false, out @namespace, out _); + => TryComputeNamespace(document, fallbackToRootNamespace, out @namespace, out _); // In general documents will have a relative path (relative to the project root). // We can only really compute a nice namespace when we know a relative path. // // However all kinds of thing are possible in tools. We shouldn't barf here if the document isn't // set up correctly. - internal static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, bool allowEmptyRootNamespace, out string @namespace, out SourceSpan? namespaceSpan) + public static bool TryComputeNamespace(this RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace, out SourceSpan? namespaceSpan) { if (document == null) { @@ -325,7 +325,7 @@ internal static bool TryComputeNamespace(this RazorCodeDocument document, bool f } else { - var result = TryComputeNamespaceCore(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: allowEmptyRootNamespace, out @namespace, out namespaceSpan); + var result = TryComputeNamespaceCore(document, fallbackToRootNamespace, out @namespace, out namespaceSpan); if (result) { document.Items[NamespaceKey] = (@namespace, namespaceSpan); @@ -336,14 +336,14 @@ internal static bool TryComputeNamespace(this RazorCodeDocument document, bool f #if DEBUG // In debug mode, even if we're cached, lets take the hit to run this again and make sure the cached value is correct. // This is to help us find issues with caching logic during development. - var validateResult = TryComputeNamespaceCore(document, fallbackToRootNamespace: fallbackToRootNamespace, allowEmptyRootNamespace: allowEmptyRootNamespace, out var validateNamespace, out _); + var validateResult = TryComputeNamespaceCore(document, fallbackToRootNamespace, out var validateNamespace, out _); Debug.Assert(validateResult, "We couldn't compute the namespace, but have a cached value, so something has gone wrong"); Debug.Assert(validateNamespace == @namespace, $"We cached a namespace of {@namespace} but calculated that it should be {validateNamespace}"); #endif return true; - bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootNamespace, bool allowEmptyRootNamespace, out string @namespace, out SourceSpan? namespaceSpan) + bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootNamespace, out string @namespace, out SourceSpan? namespaceSpan) { var filePath = document.Source.FilePath; if (filePath == null || document.Source.RelativePath == null || filePath.Length < document.Source.RelativePath.Length) @@ -409,9 +409,8 @@ bool TryComputeNamespaceCore(RazorCodeDocument document, bool fallbackToRootName baseNamespace = options?.RootNamespace; appendSuffix = true; - // null means RootNamespace wasn't set yet, so we fail for now, tooling seems to rely on this - if (baseNamespace is null || - (!allowEmptyRootNamespace && string.IsNullOrEmpty(baseNamespace))) + // Empty RootNamespace is allowed only in components. + if (!FileKinds.IsComponent(document.GetFileKind()) && string.IsNullOrEmpty(baseNamespace)) { @namespace = null; return false; From d5eeae831a3ac9564d0f5be8070d77e759d3858a Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Mar 2024 15:00:58 +0100 Subject: [PATCH 07/11] Fixup tooling --- .../Definition/DefinitionEndpoint.cs | 20 +++++++++++++++---- .../CodeActionEndToEndTest.NetFx.cs | 6 +----- ...nentAccessibilityCodeActionProviderTest.cs | 5 +++++ .../LanguageServer/LanguageServerTestBase.cs | 12 +++++++++-- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Definition/DefinitionEndpoint.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Definition/DefinitionEndpoint.cs index 1887e9df5cf..28f014bcd83 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Definition/DefinitionEndpoint.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Definition/DefinitionEndpoint.cs @@ -277,9 +277,7 @@ private async Task GetNavigateRangeAsync(IDocumentSnapshot documentSnapsh // Since we know how the compiler generates the C# source we can be a little specific here, and avoid // long tree walks. If the compiler ever changes how they generate their code, the tests for this will break // so we'll know about it. - if (root is CompilationUnitSyntax compilationUnit && - compilationUnit.Members[0] is NamespaceDeclarationSyntax namespaceDeclaration && - namespaceDeclaration.Members[0] is ClassDeclarationSyntax classDeclaration) + if (GetClassDeclaration(root) is { } classDeclaration) { var property = classDeclaration .Members @@ -303,8 +301,22 @@ compilationUnit.Members[0] is NamespaceDeclarationSyntax namespaceDeclaration && logger.LogInformation("Property found but couldn't map its location."); } - logger.LogInformation("Generated C# was not in expected shape (CompilationUnit -> Namespace -> Class)"); + logger.LogInformation("Generated C# was not in expected shape (CompilationUnit [-> Namespace] -> Class)"); return null; + + static ClassDeclarationSyntax? GetClassDeclaration(CodeAnalysis.SyntaxNode root) + { + return root switch + { + CompilationUnitSyntax unit => unit switch + { + { Members: [NamespaceDeclarationSyntax { Members: [ClassDeclarationSyntax c, ..] }, ..] } => c, + { Members: [ClassDeclarationSyntax c, ..] } => c, + _ => null, + }, + _ => null, + }; + } } } diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/CodeActionEndToEndTest.NetFx.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/CodeActionEndToEndTest.NetFx.cs index 29329496b27..4121999b00e 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/CodeActionEndToEndTest.NetFx.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/CodeActionEndToEndTest.NetFx.cs @@ -1017,11 +1017,7 @@ private async Task ValidateCodeBehindFileAsync( var diagnostics = new[] { new Diagnostic() { Code = "CS0103", Message = "The name 'DoesNotExist' does not exist in the current context" } }; TestFileMarkupParser.GetSpan(input, out input, out var textSpan); - var codeDocument = CreateCodeDocument(input, filePath: razorFilePath); - codeDocument.SetCodeGenerationOptions(RazorCodeGenerationOptions.Create(o => - { - o.RootNamespace = "Test"; - })); + var codeDocument = CreateCodeDocument(input, filePath: razorFilePath, rootNamespace: "Test"); var razorSourceText = codeDocument.GetSourceText(); var uri = new Uri(razorFilePath); var languageServer = await CreateLanguageServerAsync(codeDocument, razorFilePath); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/Razor/ComponentAccessibilityCodeActionProviderTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/Razor/ComponentAccessibilityCodeActionProviderTest.cs index 980001cae92..d2a6f11c4dd 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/Razor/ComponentAccessibilityCodeActionProviderTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/CodeActions/Razor/ComponentAccessibilityCodeActionProviderTest.cs @@ -2,6 +2,7 @@ // 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.Threading; using System.Threading.Tasks; @@ -444,16 +445,20 @@ private static RazorCodeActionContext CreateRazorCodeActionContext(VSCodeActionP var shortComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.Component", "TestAssembly"); shortComponent.CaseSensitive = true; shortComponent.TagMatchingRule(rule => rule.TagName = "Component"); + shortComponent.SetMetadata(CommonMetadata.TypeNamespace("Fully.Qualified")); var fullyQualifiedComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.Component", "TestAssembly"); fullyQualifiedComponent.CaseSensitive = true; fullyQualifiedComponent.TagMatchingRule(rule => rule.TagName = "Fully.Qualified.Component"); + fullyQualifiedComponent.SetMetadata(CommonMetadata.TypeNamespace("Fully.Qualified")); var shortGenericComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.GenericComponent", "TestAssembly"); shortGenericComponent.CaseSensitive = true; shortGenericComponent.TagMatchingRule(rule => rule.TagName = "GenericComponent"); + shortGenericComponent.SetMetadata(CommonMetadata.TypeNamespace("Fully.Qualified")); var fullyQualifiedGenericComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.GenericComponent", "TestAssembly"); fullyQualifiedGenericComponent.CaseSensitive = true; fullyQualifiedGenericComponent.TagMatchingRule(rule => rule.TagName = "Fully.Qualified.GenericComponent"); + fullyQualifiedGenericComponent.SetMetadata(CommonMetadata.TypeNamespace("Fully.Qualified")); var tagHelpers = ImmutableArray.Create(shortComponent.Build(), fullyQualifiedComponent.Build(), shortGenericComponent.Build(), fullyQualifiedGenericComponent.Build()); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs index e5082bf1f02..891c66e4513 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common.Tooling/LanguageServer/LanguageServerTestBase.cs @@ -69,7 +69,7 @@ internal RazorRequestContext CreateRazorRequestContext(VersionedDocumentContext? return requestContext; } - protected static RazorCodeDocument CreateCodeDocument(string text, ImmutableArray tagHelpers = default, string? filePath = null) + protected static RazorCodeDocument CreateCodeDocument(string text, ImmutableArray tagHelpers = default, string? filePath = null, string? rootNamespace = null) { filePath ??= "test.cshtml"; @@ -82,7 +82,15 @@ protected static RazorCodeDocument CreateCodeDocument(string text, ImmutableArra } var sourceDocument = TestRazorSourceDocument.Create(text, filePath: filePath, relativePath: filePath); - var projectEngine = RazorProjectEngine.Create(RazorExtensions.Register); + var projectEngine = RazorProjectEngine.Create(b => + { + if (rootNamespace != null) + { + b.SetRootNamespace(rootNamespace); + } + + RazorExtensions.Register(b); + }); var importDocumentName = fileKind == FileKinds.Legacy ? "_ViewImports.cshtml" : "_Imports.razor"; var defaultImportDocument = TestRazorSourceDocument.Create( """ From 7f6c75d9b6b8913a723556c0878fdcbed79e2c21 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Mar 2024 15:37:47 +0100 Subject: [PATCH 08/11] Test more formatting in global namespace --- .../CSharpStatementBlockOnTypeFormattingTest.cs | 14 ++++++++------ .../Formatting_NetFx/FormattingTestBase.cs | 10 ++++++---- .../Formatting_NetFx/RazorFormattingTest.cs | 7 ++++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/CSharpStatementBlockOnTypeFormattingTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/CSharpStatementBlockOnTypeFormattingTest.cs index a62081970f5..9e769c37366 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/CSharpStatementBlockOnTypeFormattingTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/CSharpStatementBlockOnTypeFormattingTest.cs @@ -28,8 +28,8 @@ await RunOnTypeFormattingTestAsync( triggerCharacter: '}'); } - [Fact] - public async Task CloseCurly_IfBlock_MultiLineAsync() + [Theory, CombinatorialData] + public async Task CloseCurly_IfBlock_MultiLineAsync(bool inGlobalNamespace) { await RunOnTypeFormattingTestAsync( input: """ @@ -46,11 +46,12 @@ await RunOnTypeFormattingTestAsync( } } """, - triggerCharacter: '}'); + triggerCharacter: '}', + inGlobalNamespace: inGlobalNamespace); } - [Fact] - public async Task CloseCurly_MultipleStatementBlocksAsync() + [Theory, CombinatorialData] + public async Task CloseCurly_MultipleStatementBlocksAsync(bool inGlobalNamespace) { await RunOnTypeFormattingTestAsync( input: """ @@ -79,7 +80,8 @@ await RunOnTypeFormattingTestAsync( } } """, - triggerCharacter: '}'); + triggerCharacter: '}', + inGlobalNamespace: inGlobalNamespace); } [Fact] diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs index 276b9472349..05651a80c7a 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs @@ -96,7 +96,8 @@ private protected async Task RunOnTypeFormattingTestAsync( bool insertSpaces = true, string? fileKind = null, int? expectedChangedLines = null, - RazorLSPOptions? razorLSPOptions = null) + RazorLSPOptions? razorLSPOptions = null, + bool inGlobalNamespace = false) { // Arrange fileKind ??= FileKinds.Component; @@ -106,7 +107,7 @@ private protected async Task RunOnTypeFormattingTestAsync( var razorSourceText = SourceText.From(input); var path = "file:///path/to/Document.razor"; var uri = new Uri(path); - var (codeDocument, documentSnapshot) = CreateCodeDocumentAndSnapshot(razorSourceText, uri.AbsolutePath, fileKind: fileKind); + var (codeDocument, documentSnapshot) = CreateCodeDocumentAndSnapshot(razorSourceText, uri.AbsolutePath, fileKind: fileKind, inGlobalNamespace: inGlobalNamespace); var filePathService = new FilePathService(TestLanguageServerFeatureOptions.Instance); var mappingService = new RazorDocumentMappingService( @@ -153,7 +154,8 @@ protected async Task RunCodeActionFormattingTestAsync( string expected, int tabSize = 4, bool insertSpaces = true, - string? fileKind = null) + string? fileKind = null, + bool inGlobalNamespace = false) { if (codeActionEdits is null) { @@ -168,7 +170,7 @@ protected async Task RunCodeActionFormattingTestAsync( var razorSourceText = SourceText.From(input); var path = "file:///path/to/Document.razor"; var uri = new Uri(path); - var (codeDocument, documentSnapshot) = CreateCodeDocumentAndSnapshot(razorSourceText, uri.AbsolutePath, fileKind: fileKind); + var (codeDocument, documentSnapshot) = CreateCodeDocumentAndSnapshot(razorSourceText, uri.AbsolutePath, fileKind: fileKind, inGlobalNamespace: inGlobalNamespace); var filePathService = new FilePathService(TestLanguageServerFeatureOptions.Instance); var mappingService = new RazorDocumentMappingService(filePathService, new TestDocumentContextFactory(), LoggerFactory); diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs index a9e5754d120..96fdd8f9145 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/RazorFormattingTest.cs @@ -49,8 +49,8 @@ @section Scripts { fileKind: FileKinds.Legacy); } - [Fact] - public async Task CodeBlock_SpansMultipleLines() + [Theory, CombinatorialData] + public async Task CodeBlock_SpansMultipleLines(bool inGlobalNamespace) { await RunFormattingTestAsync( input: """ @@ -74,7 +74,8 @@ private void IncrementCount() currentCount++; } } - """); + """, + inGlobalNamespace: inGlobalNamespace); } [Theory, CombinatorialData] From cfb9176c70b3a35ea6178dc1dad5116caf88f48e Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Mar 2024 16:27:41 +0100 Subject: [PATCH 09/11] Fix global namespace testing --- .../Formatting_NetFx/FormattingTestBase.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs index 05651a80c7a..a3a96042c3c 100644 --- a/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs +++ b/src/Razor/test/Microsoft.AspNetCore.Razor.LanguageServer.Test/Formatting_NetFx/FormattingTestBase.cs @@ -275,12 +275,12 @@ @using Microsoft.AspNetCore.Components.Web var imports = ImmutableArray.Create(importsSnapshot.Object); var importsDocuments = ImmutableArray.Create(importsDocument); - var documentSnapshot = CreateDocumentSnapshot(path, tagHelpers, fileKind, importsDocuments, imports, projectEngine, codeDocument); + var documentSnapshot = CreateDocumentSnapshot(path, tagHelpers, fileKind, importsDocuments, imports, projectEngine, codeDocument, inGlobalNamespace: inGlobalNamespace); return (codeDocument, documentSnapshot); } - internal static IDocumentSnapshot CreateDocumentSnapshot(string path, ImmutableArray tagHelpers, string? fileKind, ImmutableArray importsDocuments, ImmutableArray imports, RazorProjectEngine projectEngine, RazorCodeDocument codeDocument) + internal static IDocumentSnapshot CreateDocumentSnapshot(string path, ImmutableArray tagHelpers, string? fileKind, ImmutableArray importsDocuments, ImmutableArray imports, RazorProjectEngine projectEngine, RazorCodeDocument codeDocument, bool inGlobalNamespace = false) { var documentSnapshot = new Mock(MockBehavior.Strict); documentSnapshot @@ -305,9 +305,11 @@ internal static IDocumentSnapshot CreateDocumentSnapshot(string path, ImmutableA .Setup(d => d.WithText(It.IsAny())) .Returns(text => { - var sourceDocument = RazorSourceDocument.Create(text, RazorSourceDocumentProperties.Create(path, path)); + var sourceDocument = RazorSourceDocument.Create(text, RazorSourceDocumentProperties.Create( + filePath: path, + relativePath: inGlobalNamespace ? Path.GetFileName(path) : path)); var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, fileKind, importsDocuments, tagHelpers); - return CreateDocumentSnapshot(path, tagHelpers, fileKind, importsDocuments, imports, projectEngine, codeDocument); + return CreateDocumentSnapshot(path, tagHelpers, fileKind, importsDocuments, imports, projectEngine, codeDocument, inGlobalNamespace: inGlobalNamespace); }); return documentSnapshot.Object; } From ae4fc316660f0991ba06017a86e31853685e3a1a Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Wed, 20 Mar 2024 16:49:17 +0100 Subject: [PATCH 10/11] Fix formatting in global namespace --- .../Formatting/FormattingContext.cs | 9 ++++++--- .../Formatting/FormattingSpan.cs | 16 +++++++++++++++- .../Formatting/FormattingVisitor.cs | 7 +++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs index 14d96eb5b49..de4352ddd17 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingContext.cs @@ -157,6 +157,7 @@ public IReadOnlyDictionary GetIndentations() FormattingBlockKind.Markup, razorIndentationLevel: 0, htmlIndentationLevel: 0, + isInGlobalNamespace: false, isInClassBody: false, componentLambdaNestingLevel: 0); @@ -187,20 +188,22 @@ private IReadOnlyList GetFormattingSpans() if (_formattingSpans is null) { var syntaxTree = CodeDocument.GetSyntaxTree(); - _formattingSpans = GetFormattingSpans(syntaxTree); + var inGlobalNamespace = CodeDocument.TryComputeNamespace(fallbackToRootNamespace: true, out var @namespace) && + string.IsNullOrEmpty(@namespace); + _formattingSpans = GetFormattingSpans(syntaxTree, inGlobalNamespace: inGlobalNamespace); } return _formattingSpans; } - private static IReadOnlyList GetFormattingSpans(RazorSyntaxTree syntaxTree) + private static IReadOnlyList GetFormattingSpans(RazorSyntaxTree syntaxTree, bool inGlobalNamespace) { if (syntaxTree is null) { throw new ArgumentNullException(nameof(syntaxTree)); } - var visitor = new FormattingVisitor(); + var visitor = new FormattingVisitor(inGlobalNamespace: inGlobalNamespace); visitor.Visit(syntaxTree.Root); return visitor.FormattingSpans; diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingSpan.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingSpan.cs index 830901c8393..0afa79033e6 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingSpan.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingSpan.cs @@ -14,6 +14,7 @@ public FormattingSpan( FormattingBlockKind blockKind, int razorIndentationLevel, int htmlIndentationLevel, + bool isInGlobalNamespace, bool isInClassBody = false, int componentLambdaNestingLevel = 0) { @@ -23,6 +24,7 @@ public FormattingSpan( BlockKind = blockKind; RazorIndentationLevel = razorIndentationLevel; HtmlIndentationLevel = htmlIndentationLevel; + IsInGlobalNamespace = isInGlobalNamespace; IsInClassBody = isInClassBody; ComponentLambdaNestingLevel = componentLambdaNestingLevel; } @@ -41,6 +43,8 @@ public FormattingSpan( public int IndentationLevel => RazorIndentationLevel + HtmlIndentationLevel; + public bool IsInGlobalNamespace { get; } + public bool IsInClassBody { get; } public int ComponentLambdaNestingLevel { get; } @@ -49,7 +53,17 @@ public int MinCSharpIndentLevel { get { - var baseIndent = IsInClassBody ? 2 : 3; + var baseIndent = 1; + + if (!IsInGlobalNamespace) + { + baseIndent++; + } + + if (!IsInClassBody) + { + baseIndent++; + } return baseIndent + ComponentLambdaNestingLevel; } diff --git a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingVisitor.cs b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingVisitor.cs index fbe95eecd30..f670882725b 100644 --- a/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingVisitor.cs +++ b/src/Razor/src/Microsoft.AspNetCore.Razor.LanguageServer/Formatting/FormattingVisitor.cs @@ -18,6 +18,7 @@ internal class FormattingVisitor : SyntaxWalker { private const string HtmlTag = "html"; + private readonly bool _inGlobalNamespace; private readonly List _spans; private FormattingBlockKind _currentBlockKind; private SyntaxNode? _currentBlock; @@ -26,8 +27,9 @@ internal class FormattingVisitor : SyntaxWalker private int _currentComponentIndentationLevel = 0; private bool _isInClassBody = false; - public FormattingVisitor() + public FormattingVisitor(bool inGlobalNamespace) { + _inGlobalNamespace = inGlobalNamespace; _spans = new List(); _currentBlockKind = FormattingBlockKind.Markup; } @@ -481,7 +483,8 @@ private void WriteSpan(SyntaxNode node, FormattingSpanKind kind) _currentBlockKind, _currentRazorIndentationLevel, _currentHtmlIndentationLevel, - _isInClassBody, + isInGlobalNamespace: _inGlobalNamespace, + isInClassBody: _isInClassBody, _currentComponentIndentationLevel); _spans.Add(span); From 85862509f5e24b2748ea05e14a354a97eb587b0d Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Thu, 11 Apr 2024 11:15:21 +0200 Subject: [PATCH 11/11] Check global namespace flag instead of empty string --- .../src/CSharp/ComponentTagHelperDescriptorProvider.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs index 0f53faceb9f..265aef0c788 100644 --- a/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs +++ b/src/Compiler/Microsoft.CodeAnalysis.Razor.Compiler/src/CSharp/ComponentTagHelperDescriptorProvider.cs @@ -115,10 +115,9 @@ private static TagHelperDescriptor CreateNameMatchingDescriptor( if (fullyQualified) { - var containingNamespace = type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat); - var fullName = string.IsNullOrEmpty(containingNamespace) + var fullName = type.ContainingNamespace.IsGlobalNamespace ? type.Name - : $"{containingNamespace}.{type.Name}"; + : $"{type.ContainingNamespace.ToDisplayString(SymbolExtensions.FullNameTypeDisplayFormat)}.{type.Name}"; builder.TagMatchingRule(r => {