Enabling features for Components in VS
This is the final set of enabling features for VS.. including:
- Adding component types to IDE project engine
- Using file kind in the editor
- Enabling component documents in the project system
- Fixing some bugs in the xaml/msbuild authoring
- Adding a missing capability for component projects
The only thing here that probably bears explaining is the class name
mangling. This is a carry over from Blazor, basically because the
generated code is part of the workspace, we have to mangle the class
name to avoid collisions. The work to resolve this is tracked
separately, and will require coordination from a few teams to resolve.
\n\nCommit migrated from d0a8aa3f97
This commit is contained in:
parent
9c10b29f87
commit
d46aa2e2bd
|
|
@ -1,6 +1,6 @@
|
|||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
namespace AspNetCore
|
||||
namespace __BlazorGenerated
|
||||
{
|
||||
#line hidden
|
||||
using TModel = global::System.Object;
|
||||
|
|
@ -9,7 +9,7 @@ namespace AspNetCore
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
public class BasicComponent : Microsoft.AspNetCore.Components.ComponentBase, IDisposable
|
||||
public class AspNetCore_d3c3d6059615673cb46fc4974164d61eabadb890 : Microsoft.AspNetCore.Components.ComponentBase, IDisposable
|
||||
{
|
||||
#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
Document -
|
||||
NamespaceDeclaration - - AspNetCore
|
||||
NamespaceDeclaration - - __BlazorGenerated
|
||||
UsingDirective - - TModel = global::System.Object
|
||||
UsingDirective - (1:0,1 [12] ) - System
|
||||
UsingDirective - (16:1,1 [32] ) - System.Collections.Generic
|
||||
UsingDirective - (51:2,1 [17] ) - System.Linq
|
||||
UsingDirective - (71:3,1 [28] ) - System.Threading.Tasks
|
||||
UsingDirective - (102:4,1 [37] ) - Microsoft.AspNetCore.Components
|
||||
ClassDeclaration - - public - BasicComponent - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
ClassDeclaration - - public - AspNetCore_d3c3d6059615673cb46fc4974164d61eabadb890 - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
DesignTimeDirective -
|
||||
DirectiveToken - (14:0,14 [36] ) - "*, Microsoft.AspNetCore.Components"
|
||||
DirectiveToken - (12:0,12 [11] BasicComponent.cshtml) - IDisposable
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
Source Location: (12:0,12 [11] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|IDisposable|
|
||||
Generated Location: (618:17,0 [11] )
|
||||
Generated Location: (662:17,0 [11] )
|
||||
|IDisposable|
|
||||
|
||||
Source Location: (38:1,13 [15] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|this.ToString()|
|
||||
Generated Location: (1214:33,13 [15] )
|
||||
Generated Location: (1258:33,13 [15] )
|
||||
|this.ToString()|
|
||||
|
||||
Source Location: (79:3,5 [29] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|string.Format("{0}", "Hello")|
|
||||
Generated Location: (1359:38,6 [29] )
|
||||
Generated Location: (1403:38,6 [29] )
|
||||
|string.Format("{0}", "Hello")|
|
||||
|
||||
Source Location: (132:6,12 [37] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|
|
||||
void IDisposable.Dispose(){ }
|
||||
|
|
||||
Generated Location: (1573:45,12 [37] )
|
||||
Generated Location: (1617:45,12 [37] )
|
||||
|
|
||||
void IDisposable.Dispose(){ }
|
||||
|
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d3c3d6059615673cb46fc4974164d61eabadb890"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
namespace AspNetCore
|
||||
namespace __BlazorGenerated
|
||||
{
|
||||
#line hidden
|
||||
using System;
|
||||
|
|
@ -9,7 +9,7 @@ namespace AspNetCore
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
public class BasicComponent : Microsoft.AspNetCore.Components.ComponentBase, IDisposable
|
||||
public class AspNetCore_d3c3d6059615673cb46fc4974164d61eabadb890 : Microsoft.AspNetCore.Components.ComponentBase, IDisposable
|
||||
{
|
||||
#pragma warning disable 1998
|
||||
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
Document -
|
||||
NamespaceDeclaration - - AspNetCore
|
||||
NamespaceDeclaration - - __BlazorGenerated
|
||||
UsingDirective - (1:0,1 [14] ) - System
|
||||
UsingDirective - (16:1,1 [34] ) - System.Collections.Generic
|
||||
UsingDirective - (51:2,1 [19] ) - System.Linq
|
||||
UsingDirective - (71:3,1 [30] ) - System.Threading.Tasks
|
||||
UsingDirective - (102:4,1 [37] ) - Microsoft.AspNetCore.Components
|
||||
ClassDeclaration - - public - BasicComponent - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
ClassDeclaration - - public - AspNetCore_d3c3d6059615673cb46fc4974164d61eabadb890 - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
MethodDeclaration - - protected override - void - BuildRenderTree
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - base.BuildRenderTree(builder);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,25 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
private static readonly char[] PathSeparators = new char[] { '/', '\\' };
|
||||
private static readonly char[] NamespaceSeparators = new char[] { '.' };
|
||||
|
||||
/// <summary>
|
||||
/// The base namespace.
|
||||
/// </summary>
|
||||
// This is a fallback value and will only be used if we can't compute
|
||||
// a reasonable namespace.
|
||||
public string BaseNamespace { get; set; } = "__BlazorGenerated";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to mangle class names.
|
||||
///
|
||||
/// Set to true in the IDE so we can generated mangled class names. This is needed
|
||||
/// to avoid conflicts between generated design-time code and the code in the editor.
|
||||
///
|
||||
/// A better workaround for this would be to create a singlefilegenerator that overrides
|
||||
/// the codegen process when a document is open, but this is more involved, so hacking
|
||||
/// it for now.
|
||||
/// </summary>
|
||||
public bool MangleClassNames { get; set; } = false;
|
||||
|
||||
protected override string DocumentKind => ComponentDocumentKind;
|
||||
|
||||
// Ensure this runs before the MVC classifiers which have Order = 0
|
||||
|
|
@ -32,40 +51,35 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return new ComponentCodeTarget(options, TargetExtensions);
|
||||
}
|
||||
|
||||
protected override void OnDocumentStructureCreated(RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method)
|
||||
/// <inheritdoc />
|
||||
protected override void OnDocumentStructureCreated(
|
||||
RazorCodeDocument codeDocument,
|
||||
NamespaceDeclarationIntermediateNode @namespace,
|
||||
ClassDeclarationIntermediateNode @class,
|
||||
MethodDeclarationIntermediateNode method)
|
||||
{
|
||||
base.OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
|
||||
|
||||
if (!TryComputeNamespaceAndClass(
|
||||
codeDocument.Source.FilePath,
|
||||
codeDocument.Source.RelativePath,
|
||||
out var computedNamespace,
|
||||
out var computedClass))
|
||||
codeDocument.Source.FilePath,
|
||||
codeDocument.Source.RelativePath,
|
||||
out var computedNamespace,
|
||||
out var computedClass))
|
||||
{
|
||||
// If we can't compute a nice namespace (no relative path) then just generate something
|
||||
// mangled.
|
||||
computedNamespace = "AspNetCore";
|
||||
computedNamespace = BaseNamespace;
|
||||
var checksum = Checksum.BytesToString(codeDocument.Source.GetChecksum());
|
||||
computedClass = $"AspNetCore_{checksum}";
|
||||
}
|
||||
|
||||
if (MangleClassNames)
|
||||
{
|
||||
computedClass = "__" + computedClass;
|
||||
}
|
||||
|
||||
@namespace.Content = computedNamespace;
|
||||
|
||||
@class.BaseType = ComponentsApi.ComponentBase.FullTypeName;
|
||||
@class.ClassName = computedClass;
|
||||
@class.BaseType = $"{CodeGenerationConstants.ComponentBase.FullTypeName}";
|
||||
var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath;
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
// It's possible for a Razor document to not have a file path.
|
||||
// Eg. When we try to generate code for an in memory document like default imports.
|
||||
var checksum = Checksum.BytesToString(codeDocument.Source.GetChecksum());
|
||||
@class.ClassName = $"AspNetCore_{checksum}";
|
||||
}
|
||||
else
|
||||
{
|
||||
@class.ClassName = CSharpIdentifier.SanitizeIdentifier(Path.GetFileNameWithoutExtension(filePath));
|
||||
}
|
||||
|
||||
@class.Modifiers.Clear();
|
||||
@class.Modifiers.Add("public");
|
||||
|
||||
|
|
@ -82,8 +96,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
@class.TypeParameters.Add(new TypeParameter() { ParameterName = typeParamNode.Tokens.First().Content, });
|
||||
}
|
||||
|
||||
method.MethodName = CodeGenerationConstants.ComponentBase.BuildRenderTree;
|
||||
method.ReturnType = "void";
|
||||
method.MethodName = ComponentsApi.ComponentBase.BuildRenderTree;
|
||||
method.Modifiers.Clear();
|
||||
method.Modifiers.Add("protected");
|
||||
method.Modifiers.Add("override");
|
||||
|
|
@ -91,8 +105,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
method.Parameters.Clear();
|
||||
method.Parameters.Add(new MethodParameter()
|
||||
{
|
||||
TypeName = CodeGenerationConstants.RenderTreeBuilder.FullTypeName,
|
||||
ParameterName = CodeGenerationConstants.ComponentBase.BuildRenderTreeParameter,
|
||||
ParameterName = "builder",
|
||||
TypeName = ComponentsApi.RenderTreeBuilder.FullTypeName,
|
||||
});
|
||||
|
||||
// We need to call the 'base' method as the first statement.
|
||||
|
|
@ -101,11 +115,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
callBase.Children.Add(new IntermediateToken
|
||||
{
|
||||
Kind = TokenKind.CSharp,
|
||||
Content = $"base.{CodeGenerationConstants.ComponentBase.BuildRenderTree}({CodeGenerationConstants.ComponentBase.BuildRenderTreeParameter});"
|
||||
Content = $"base.{ComponentsApi.ComponentBase.BuildRenderTree}(builder);"
|
||||
});
|
||||
method.Children.Insert(0, callBase);
|
||||
}
|
||||
|
||||
// In general documents will have a relative path (relative to the project root).
|
||||
// We can only really compute a nice class/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.
|
||||
private bool TryComputeNamespaceAndClass(string filePath, string relativePath, out string @namespace, out string @class)
|
||||
{
|
||||
if (filePath == null || relativePath == null || filePath.Length <= relativePath.Length)
|
||||
|
|
@ -158,4 +177,4 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
internal static bool IsBuildRenderTreeBaseCall(CSharpCodeIntermediateNode node)
|
||||
=> node.Annotations[BuildRenderTreeBaseCallAnnotation] != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,7 +207,9 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
var attribute = attributes[j];
|
||||
|
||||
if (attribute.AttributeClass == bindElement)
|
||||
// We need to check the constructor argument length here, because this can show up as 0
|
||||
// if the language service fails to initialize. This is an invalid case, so skip it.
|
||||
if (attribute.AttributeClass == bindElement && attribute.ConstructorArguments.Length == 4)
|
||||
{
|
||||
results.Add(new ElementBindData(
|
||||
type.ContainingAssembly.Name,
|
||||
|
|
@ -218,7 +220,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
(string)attribute.ConstructorArguments[2].Value,
|
||||
(string)attribute.ConstructorArguments[3].Value));
|
||||
}
|
||||
else if (attribute.AttributeClass == bindInputElement)
|
||||
else if (attribute.AttributeClass == bindInputElement && attribute.ConstructorArguments.Length == 4)
|
||||
{
|
||||
results.Add(new ElementBindData(
|
||||
type.ContainingAssembly.Name,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
Condition="'%(RazorComponentWithTargetPath.GeneratedDeclaration)' == ''" />
|
||||
|
||||
<ItemGroup>
|
||||
<_RazorComponentDeclaration Include="%(RazorComponentWithTargetPath.GeneratedDeclaration)" />
|
||||
<_RazorComponentDeclaration Include="@(RazorComponentWithTargetPath->'%(GeneratedDeclaration)')">
|
||||
<DependentUpon>%(Identity)</DependentUpon>
|
||||
</_RazorComponentDeclaration>
|
||||
<_RazorComponentDefinition Include="%(RazorComponentWithTargetPath.GeneratedOutput)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,17 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
<ProjectCapability Include="DotNetCoreRazorConfiguration" Condition="'$(_TargetingNETCoreApp30OrLater)'=='true'"/>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
For now we need to treat component files as if they have a single file generator. This will allow us
|
||||
to trigger a workspace update for the declaration files when they change.
|
||||
-->
|
||||
<ItemGroup>
|
||||
<Content Update="@(RazorComponent)">
|
||||
<Generator>MSBuild:RazorGenerateComponentDeclarationDesignTime</Generator>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
WebSdk imports these capabilities for nesting in DotNetCoreWeb projects.
|
||||
Conditinally import these capabilities if the project isn't targeting the WebSdk.
|
||||
|
|
@ -69,4 +80,10 @@ Copyright (c) .NET Foundation. All rights reserved.
|
|||
Returns="@(RazorComponentWithTargetPath)">
|
||||
</Target>
|
||||
|
||||
<!-- Called by the project system to update generated declaration files -->
|
||||
<Target
|
||||
Name="RazorGenerateComponentDeclarationDesignTime"
|
||||
DependsOnTargets="ResolveRazorConfiguration;ResolveRazorComponentInputs;AssignRazorComponentTargetPaths;RazorGenerateComponentDeclaration">
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
Loading…
Reference in New Issue