Skip to content

Add feature to show users what global-usings are in scope at the top of a file.#60893

Merged
CyrusNajmabadi merged 31 commits intodotnet:mainfrom
CyrusNajmabadi:globalUsingsMargin
Apr 30, 2022
Merged

Add feature to show users what global-usings are in scope at the top of a file.#60893
CyrusNajmabadi merged 31 commits intodotnet:mainfrom
CyrusNajmabadi:globalUsingsMargin

Conversation

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor

@CyrusNajmabadi CyrusNajmabadi commented Apr 21, 2022

Depends on #59533. So Draft PR for now.

I'm reusing 'inheritance' margin because (if you squint) you're "inheriting" these using-directives from the global scope.

Looks like this:

image

When clicking on that item, you get:

image

Clicking on any item takes you to the declaration location for it:

image

If you have multiple files bringing in directives, this shows up as:

image

Todo:

  • Tests

@ghost ghost added the Area-Compilers label Apr 21, 2022
@CyrusNajmabadi CyrusNajmabadi changed the title WIP. Global usings. Add feature to show users what global-usings are in scope at the top of a file. Apr 22, 2022
@CyrusNajmabadi CyrusNajmabadi marked this pull request as ready for review April 28, 2022 21:30
@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner April 28, 2022 21:30
@CyrusNajmabadi CyrusNajmabadi requested a review from Cosifne April 28, 2022 21:30
if (allDeclarationNodes.IsEmpty)

var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
var imports = syntaxFacts.GetImportsOfCompilationUnit(root);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will likely need to move this OOP.

=> new(
member.DisplayTexts.JoinText(),
member.Glyph.GetImageMoniker(),
InheritanceMarginHelpers.CreateModelsForTargetItems(member.TargetItems));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

inlined to single caller.

Copy link
Copy Markdown
Member

@davidwengier davidwengier left a comment

Choose a reason for hiding this comment

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

you're "inheriting" these using-directives

I'll remember this. Next time you tell me I put something in the wrong place, you just watch out!

// project level imports come first.
(null, not null) => -1,
(not null, null) => 1,
// both are from different files. Sort by file path first, then location in file if same file path.
Copy link
Copy Markdown
Member

@davidwengier davidwengier Apr 29, 2022

Choose a reason for hiding this comment

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

I wonder if this distinction is useful, or if just sorting them all alphabetically is just as good?

Related: By "project level" do you mean the VB global imports that are specified on the command line? Because if so, isn't it impossible for "project level imports" and "global usings" to appear int he same list, since they're relevant to different languages?
Update: After looking at the rest of the PR, yes, the above is true. So I guess my comment is "this could potentially just be simplified to sort by namespace and nobody will care". Potentially.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I wonder if this distinction is useful, or if just sorting them all alphabetically is just as good?

I def like sorting as per the items in a file, as that makes it feel like you're getting a little view into that file :)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Related: By "project level" do you mean the VB global imports that are specified on the command line? Because if so, isn't it impossible for "project level imports" and "global usings" to appear int he same list, since they're relevant to different languages?

you are correct. the above is a little bit of paranoia... i'll see if i can comfortably clean it up though!

InheritanceRelationship.OverriddenMember => ServicesVSResources.Overridden_members,
InheritanceRelationship.OverridingMember => ServicesVSResources.Overriding_members,
InheritanceRelationship.ImplementingMember => ServicesVSResources.Implementing_members,
InheritanceRelationship.InheritedImport => item.DisplayTexts.JoinText(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this need localization?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nope! this is already localized.

DisplayTexts = displayTexts;
Glyph = glyph;
TargetItems = targetItems;
TargetItems = isOrdered ? targetItems : targetItems.OrderBy(item => item.DisplayName).ToImmutableArray();
Copy link
Copy Markdown
Member

@Cosifne Cosifne Apr 29, 2022

Choose a reason for hiding this comment

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

Seems like a place might be benefited from our offline discussion. (Move all the data processing part to OOP)
I am fine with this approach now, but could you add a comment here to explain isOrdered would only be used for showing the global using?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yup. i will move this OOP and add the comment.

/// </summary>
public readonly int LineNumber;

public readonly string? TopLevelDisplayText;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similar comments as other comment, could you add a comment here to explain this would only be used showing global usings?
I would help when later I start refactoring this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will do!

@Cosifne
Copy link
Copy Markdown
Member

Cosifne commented Apr 29, 2022

InheritanceMargin is controlled by this Option https://sourceroslyn.io/#Microsoft.VisualStudio.LanguageServices/InheritanceMargin/InheritanceMarginTaggerProvider.cs,100

Do you feel it would be fine to also let this feature controlled by this option?
Also the generation time of the tag would be put under https://sourceroslyn.io/#Microsoft.VisualStudio.LanguageServices/InheritanceMargin/InheritanceMarginTaggerProvider.cs,128

@Cosifne
Copy link
Copy Markdown
Member

Cosifne commented Apr 29, 2022

The overall approach LGTM. I guess there are still some in progress works so just ping me after it's done I can take a second look.

Two potential future items (not blocking issue for this features) in my mind:

  1. I feel generally we are bringing more and more contents to inheritance margin. This would not be a big problem for this PR, because namespace generally won't stay on the same line with class/method/property. But we might want to look for some general approach to handle the overlapping problem.
  2. We need a big refactoring to move all things except the ViewModel to OOP : )

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

Do you feel it would be fine to also let this feature controlled by this option?

i think i'll add an option for this.

@@ -1,107 +0,0 @@
// Licensed to the .NET Foundation under one or more agreements.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this was moved down to Features. Unfortunatelly, github seems to view it as a delete :(

namespace Microsoft.CodeAnalysis.CSharp.InheritanceMargin
{
[ExportLanguageService(typeof(IInheritanceMarginService), LanguageNames.CSharp), Shared]
internal class CSharpInheritanceMarginService : AbstractInheritanceMarginService
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

these types moved to Features level. That way they can also be used by the remote service, while still utilizing inheritance (instead of statics).

[DataContract]
internal readonly struct DocumentIdSpan
{
private readonly Workspace _workspace;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

remove workspace. That allowed thsi type to become serializable, which made it so all the types in inheritance margin can be serializable themselves (no need for a duplicate hierarchy).

public async ValueTask<ImmutableArray<InheritanceMarginItem>> GetInheritanceMemberItemsAsync(
Document document,
TextSpan spanToSearch,
CancellationToken cancellationToken)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this was a move, but the logic here did change somewhat. namely that we can still call into OOP, even if we have no membersymbolkeys (since we may have global imports).

private async ValueTask<(Project remapped, SymbolKeyAndLineNumberArray symbolKeyAndLineNumbers)> GetMemberSymbolKeysAsync(
Document document,
TextSpan spanToSearch,
CancellationToken cancellationToken)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this was an extract method. however, isntead of calling into oop, it determines what data it wants to call into oop with.

namespace Microsoft.CodeAnalysis.InheritanceMargin
{
internal static class InheritanceMarginServiceHelper
internal abstract partial class AbstractInheritanceMarginService
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this became non-static and a partial part of AbstractInheritanceMarginService. taht way it can benefit from inheritance for when we want C# and VB to differ in behavior (for example, titles of htings).

TextSpan spanToSearch,
ArrayBuilder<InheritanceMarginItem> items,
CancellationToken cancellationToken)
{
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this is new logic for this PR.

namespace Microsoft.CodeAnalysis.InheritanceMargin
{
[DataContract]
internal readonly struct InheritanceMarginItem
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

made these types into message-pack compatible types. this allowes us to get rid of the SerializableXXX siblings.

@CyrusNajmabadi
Copy link
Copy Markdown
Contributor Author

@Cosifne this is ready for review.

@CyrusNajmabadi CyrusNajmabadi requested a review from a team as a code owner April 29, 2022 19:32
@CyrusNajmabadi CyrusNajmabadi merged commit 6d54bd9 into dotnet:main Apr 30, 2022
@ghost ghost added this to the Next milestone Apr 30, 2022
@CyrusNajmabadi CyrusNajmabadi deleted the globalUsingsMargin branch April 30, 2022 02:03
@Cosifne Cosifne modified the milestones: Next, 17.3 P2 May 31, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants