Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FUSE] Provide intellisense for @inject directives #10771

Merged
merged 6 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ public class MyService<TModel>
Assert.NotEmpty(compiled.CodeDocument.GetCSharpDocument().Diagnostics);
}

[Fact]
public void IncompleteDirectives_Runtime()
{
// Arrange
AddCSharpSyntaxTree(@"
public class MyService<TModel>
{
public string Html { get; set; }
}");

var projectItem = CreateProjectItemFromFile();

// Act
var compiled = CompileToCSharp(projectItem, designTime: false);

// Assert
AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode());
AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument());
AssertSourceMappingsMatchBaseline(compiled.CodeDocument);

// We expect this test to generate a bunch of errors.
Assert.True(compiled.CodeDocument.GetCSharpDocument().Diagnostics.Length > 0);
}

[Fact]
public void InheritsViewModel_DesignTime()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,6 @@
Inject -
Inject -
Inject -
Inject -
Inject -
Inject -
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "844eb91b909a14b78feddd5e6866563b5a75e021"
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml" "{8829d00f-11b8-4213-878b-770e8597ac16}" "c83d1c26cf039a87fc6aedc860fd9d28a34d96dfb2e405e6af3918602ca27755"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These files existed, because apparently the test did at one point, too, but they were super out of date, so I would just review these as newly added.

// <auto-generated/>
#pragma warning disable 1591
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCore.TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives), @"mvc.1.0.view", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml")]
namespace AspNetCore
{
#line hidden
#line default
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives_cshtml : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
#line default
#line hidden
[global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"Sha256", @"c83d1c26cf039a87fc6aedc860fd9d28a34d96dfb2e405e6af3918602ca27755", @"/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml")]
public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
{
#pragma warning disable 1998
public async override global::System.Threading.Tasks.Task ExecuteAsync()
{
BeginContext(83, 4, true);
WriteLiteral("\r\n\r\n");
EndContext();
BeginContext(93, 2, true);
WriteLiteral("\r\n");
EndContext();
BeginContext(102, 4, true);
WriteLiteral("\r\n");
WriteLiteral("\r\n\r\n");
EndContext();
BeginContext(113, 2, true);
WriteLiteral("\r\n");
EndContext();
BeginContext(123, 2, true);
WriteLiteral("\r\n");
EndContext();
BeginContext(150, 2, true);
WriteLiteral("\r\n");
EndContext();
}
#pragma warning restore 1998
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public
#line (8,9)-(8,18) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"
MyService<dynamic>

#line default
#line hidden
Member_test { get; private set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this test and not __UniqueIdSuppressedForTesting__?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wen have two places where we set it. A whole bunch of other places were using the other one, so it made the diff huge. I'll submit a follow up PR to make them consistent.

#line (7,9)-(7,9) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"

#line default
#line hidden
#line (6,8)-(6,8) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"

#line default
#line hidden
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(3,7): Error RZ9999: The 'model' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(4,1): Error RZ9999: The 'model' directive may only occur once per document.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(4,8): Error RZ9999: The 'model' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(6,8): Error RZ9999: The 'inject' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(7,9): Error RZ9999: The 'inject' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(8,26): Error RZ9999: The 'inject' directive expects an identifier.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(3,7): Error RZ1013: The 'model' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(4,1): Error RZ2001: The 'model' directive may only occur once per document.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(4,8): Error RZ1013: The 'model' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(6,8): Error RZ1013: The 'inject' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(7,9): Error RZ1013: The 'inject' directive expects a type name.
TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml(8,26): Error RZ1015: The 'inject' directive expects an identifier.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Document -
RazorCompiledItemAttribute -
NamespaceDeclaration - - AspNetCore
UsingDirective - (1:0,1 [14] ) - System
UsingDirective - (16:1,1 [34] ) - System.Collections.Generic
Expand All @@ -7,50 +8,36 @@
UsingDirective - (102:4,1 [32] ) - Microsoft.AspNetCore.Mvc
UsingDirective - (135:5,1 [42] ) - Microsoft.AspNetCore.Mvc.Rendering
UsingDirective - (178:6,1 [45] ) - Microsoft.AspNetCore.Mvc.ViewFeatures
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives_cshtml - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic> -
RazorSourceChecksumAttribute -
ClassDeclaration - - public - TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic> -
MethodDeclaration - - public async override - global::System.Threading.Tasks.Task - ExecuteAsync
CSharpCode -
IntermediateToken - - CSharp - BeginContext(83, 4, true);
HtmlContent - (83:0,83 [4] IncompleteDirectives.cshtml)
IntermediateToken - (83:0,83 [4] IncompleteDirectives.cshtml) - Html - \n\n
CSharpCode -
IntermediateToken - - CSharp - EndContext();
HtmlContent - (85:1,0 [2] IncompleteDirectives.cshtml)
LazyIntermediateToken - (85:1,0 [2] IncompleteDirectives.cshtml) - Html - \n
MalformedDirective - (87:2,0 [6] IncompleteDirectives.cshtml) - model
CSharpCode -
IntermediateToken - - CSharp - BeginContext(93, 2, true);
CSharpCode - (93:2,6 [0] IncompleteDirectives.cshtml)
LazyIntermediateToken - (93:2,6 [0] IncompleteDirectives.cshtml) - CSharp -
HtmlContent - (93:2,6 [2] IncompleteDirectives.cshtml)
IntermediateToken - (93:2,6 [2] IncompleteDirectives.cshtml) - Html - \n
CSharpCode -
IntermediateToken - - CSharp - EndContext();
LazyIntermediateToken - (93:2,6 [2] IncompleteDirectives.cshtml) - Html - \n
MalformedDirective - (95:3,0 [7] IncompleteDirectives.cshtml) - model
CSharpCode -
IntermediateToken - - CSharp - BeginContext(102, 4, true);
DirectiveToken - (102:3,7 [0] IncompleteDirectives.cshtml) -
HtmlContent - (102:3,7 [4] IncompleteDirectives.cshtml)
IntermediateToken - (102:3,7 [4] IncompleteDirectives.cshtml) - Html - \n\n
CSharpCode -
IntermediateToken - - CSharp - EndContext();
LazyIntermediateToken - (102:3,7 [4] IncompleteDirectives.cshtml) - Html - \n\n
MalformedDirective - (106:5,0 [7] IncompleteDirectives.cshtml) - inject
CSharpCode -
IntermediateToken - - CSharp - BeginContext(113, 2, true);
CSharpCode - (113:5,7 [0] IncompleteDirectives.cshtml)
LazyIntermediateToken - (113:5,7 [0] IncompleteDirectives.cshtml) - CSharp -
HtmlContent - (113:5,7 [2] IncompleteDirectives.cshtml)
IntermediateToken - (113:5,7 [2] IncompleteDirectives.cshtml) - Html - \n
CSharpCode -
IntermediateToken - - CSharp - EndContext();
LazyIntermediateToken - (113:5,7 [2] IncompleteDirectives.cshtml) - Html - \n
MalformedDirective - (115:6,0 [8] IncompleteDirectives.cshtml) - inject
CSharpCode -
IntermediateToken - - CSharp - BeginContext(123, 2, true);
DirectiveToken - (123:6,8 [0] IncompleteDirectives.cshtml) -
HtmlContent - (123:6,8 [2] IncompleteDirectives.cshtml)
IntermediateToken - (123:6,8 [2] IncompleteDirectives.cshtml) - Html - \n
CSharpCode -
IntermediateToken - - CSharp - EndContext();
LazyIntermediateToken - (123:6,8 [2] IncompleteDirectives.cshtml) - Html - \n
MalformedDirective - (125:7,0 [25] IncompleteDirectives.cshtml) - inject
DirectiveToken - (133:7,8 [17] IncompleteDirectives.cshtml) - MyService<TModel>
CSharpCode -
IntermediateToken - - CSharp - BeginContext(150, 2, true);
HtmlContent - (150:7,25 [2] IncompleteDirectives.cshtml)
IntermediateToken - (150:7,25 [2] IncompleteDirectives.cshtml) - Html - \n
CSharpCode -
IntermediateToken - - CSharp - EndContext();
LazyIntermediateToken - (150:7,25 [2] IncompleteDirectives.cshtml) - Html - \n
Inject -
Inject -
Inject -
Inject -
Inject -
Inject -
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Source Location: (133:7,8 [9] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml)
|MyService|
Generated Location: (1895:33,0 [9] )
|MyService|

Source Location: (123:6,8 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml)
||
Generated Location: (2096:39,0 [0] )
||

Source Location: (113:5,7 [0] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml)
||
Generated Location: (2233:43,0 [0] )
||

Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
Inject -
Inject -
Inject -
Inject -
Inject -
Inject -
CSharpCode -
IntermediateToken - - CSharp - public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives> ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives>)PageContext?.ViewData;
CSharpCode -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_Incomplete
EndContext();
}
#pragma warning restore 1998
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public
#line (12,9)-(12,18) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"
MyService<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives>

#line default
#line hidden
Member_test { get; private set; }
#line (11,9)-(11,9) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"

#line default
#line hidden
#line (10,8)-(10,8) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"

#line default
#line hidden
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
Inject -
Inject -
Inject -
Inject -
Inject -
Inject -
CSharpCode -
IntermediateToken - - CSharp - public global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives> ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives>)PageContext?.ViewData;
CSharpCode -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
Inject -
Inject -
Inject -
Inject -
Inject -
Inject -
CSharpCode -
IntermediateToken - - CSharp - #nullable restore\npublic global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives> ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives>)PageContext?.ViewData!;\n#nullable disable
CSharpCode -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,29 @@ public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_Incomplete
WriteLiteral("\r\n");
}
#pragma warning restore 1998
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public
#nullable restore
#line (12,9)-(12,18) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"
MyService<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives>

#line default
#line hidden
#nullable disable
Member_test { get; private set; }
= default!;
#nullable restore
#line (11,9)-(11,9) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"

#line default
#line hidden
#nullable disable
#nullable restore
#line (10,8)-(10,8) "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/IncompleteDirectives.cshtml"

#line default
#line hidden
#nullable disable
#nullable restore
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; } = default!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
Inject -
Inject -
Inject -
Inject -
Inject -
Inject -
CSharpCode -
IntermediateToken - - CSharp - #nullable restore\npublic global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives> ViewData => (global::Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary<TestFiles_IntegrationTests_CodeGenerationIntegrationTest_IncompleteDirectives>)PageContext?.ViewData!;\n#nullable disable
CSharpCode -
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,23 @@ public void SupportsInjectDirective()
s => AssertEx.Equal("private TestNamespace.IMyService2 Test.TestComponent.MyService2 { get; set; }", s.ToTestDisplayString()));
}

[Fact]
public void SupportsIncompleteInjectDirectives()
{
var component = CompileToComponent("""
@inject
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me how this resolves the issue. The issue talks about IntelliSense on the type part and when user writes @inject and space we still don't generate anything, i.e., nothing has really changed for this sceario, how could the issue be fixed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am also confused here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's always been true. You don't get IntelliSense until you type the first character today. Once you type the first character, you get it, but in fuse you never get it until you start typing the name:

Not-fuse:

not_fuse

Fuse (broken):

fuse_broken

Fuse (fixed):

fuse_fixed

Note, this is same behavior you get today in C#, if you're e.g. writing a new property:

csharp_property

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In C#, can't you manually bring up completion though? What's the equivalent behavior in razor currently?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok, so if you invoke IntelliSense in either the C# or non-fuse razor case, you just get an empty IntelliSense box.
image

In the PR right now you get nothing, because we have nothing emitted. In design time that works by mapping to just a totally empty space. We can probably do that here too to get exactly the same behavior.

Copy link
Contributor Author

@chsienki chsienki Aug 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, actually this is proving much harder than expected. Not necessarily in generating the code but trying to figure out what exactly we should generate to make IntelliSense happy. I'll chat with @davidwengier next week and try and figure it out.

Should we just take this PR as-is as it makes most of the scenario better, and file a follow up issue to fix the empty space part?

Lol, wrote this then almost immediately figured it out. Update incoming.

@inject DateTime
@inject DateTime Value
""");

// Assert 1: Compiled type has correct properties
var injectableProperties = component.GetMembers().OfType<IPropertySymbol>()
.Where(p => p.GetAttributes().Any(a => a.AttributeClass.Name == "InjectAttribute"));
Assert.Collection(injectableProperties.OrderBy(p => p.Name),
s => AssertEx.Equal("private System.DateTime Test.TestComponent.Member___UniqueIdSuppressedForTesting__ { get; set; }", s.ToTestDisplayString()),
s => AssertEx.Equal("private System.DateTime Test.TestComponent.Value { get; set; }", s.ToTestDisplayString()));
}

private const string AdditionalCode =
"""
using Microsoft.AspNetCore.Components;
Expand Down
Loading