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

Conversation

chsienki
Copy link
Contributor

@chsienki chsienki commented Aug 20, 2024

With fuse enabled, it was noticed that typing @inject was not providing IntelliSense on the type.

The directive is @inject <type> <name> with type and name being two different DirectiveTokens. Due to this structure, it's effectively malformed until you begin typing the name. Previously in runtime we don't emit anything for a malformed inject directive. Until you start typing the name, there is no C# to actually map back to the razor document and provide IntelliSense with. Design time works around this by emitting the two tokens as separate design time only elements, so there is always some C# even when the directive is incomplete.

This PR handles malformed @inject directives, and effectively generates an 'error token' that means we can emit valid C# code in these cases. When the user has not yet typed a member name, but started the type name, we begin emitting the full code with a member called Member_<randomGeneratedId>. We still provide an RZ diagnostic, so the code will not compile, but we can still emit something that is valid so we can provide IntelliSense to the user.

I tried adding a malformed Component integration test, but design time is failing with missing pragmas. I tried the test on main, and it failed there too, so nothing to do with these changes. Will try and figure it out, but don't want to block on it.
 
Fixes https://devdiv.visualstudio.com/DevDiv/_queries/edit/2172170/

@@ -1,42 +1,42 @@
#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.

@chsienki chsienki force-pushed the fuse_fixes branch 2 times, most recently from 1d7e136 to 025684c Compare August 22, 2024 18:47
@chsienki chsienki changed the title Fuse fixes [FUSE] Provide intellisense for @inject directives Aug 22, 2024
@chsienki
Copy link
Contributor Author

@dotnet/razor-compiler for review please :)

@chsienki chsienki marked this pull request as ready for review August 22, 2024 19:13
@chsienki chsienki requested review from a team as code owners August 22, 2024 19:13
@davidwengier davidwengier removed the request for review from a team August 23, 2024 01:41
@@ -650,10 +646,13 @@ private string GetContent(HtmlContentIntermediateNode node)
// Internal for testing
internal static string GetDeterministicId(CodeRenderingContext context)
Copy link
Member

Choose a reason for hiding this comment

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

Should this be moved somewhere else? It's not used for tag helpers only anymore.


public void Configure(RazorCodeGenerationOptionsBuilder options)
{
options.SuppressUniqueIds = "test";
Copy link
Member

Choose a reason for hiding this comment

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

Consider making this something more recognizable like "TestSuppressedUniqueId"

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.

defaultValue: true);
var memberName = MemberName ?? "Member_" + DefaultTagHelperTargetExtension.GetDeterministicId(context);

if (!context.Options.DesignTime || !IsMalformed)
Copy link
Member

Choose a reason for hiding this comment

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

Will it mess up the existing design-time experience if we just unconditionally write this property?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, it shouldn't, I've just been avoiding making any changes to the design time where possible.

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.

I am also confused here.

@chsienki
Copy link
Contributor Author

@333fred @jjonescz Ok, updated it so you get IntelliSense if you request it even before you've typed a type name at all.

@@ -36,6 +36,14 @@ public class TestFiles_IntegrationTests_CodeGenerationIntegrationTest_Incomplete
#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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants