Codist icon indicating copy to clipboard operation
Codist copied to clipboard

Suggestion: Improved Scopes for Inherited/Implemented/Extension Members

Open fitdev opened this issue 1 month ago • 12 comments

It's very useful to limit the find symbol results for searches like Find Referrers/Overrides/Extensions to the following cases:

  • Current project only (perhaps have Shift modifier for this)
  • Current + Derived only
  • Current type only (for generic extensions) - hugely useful as an extension may be valid at 100s of places, but I am only interested in a few of them (closed generics) that use specific this type. (obviously it would have to be invoked from the caller, as the callee is an open generic).

And so with this approach these extra modifiers could be combined to further limit the scope.

The generics would be especially useful for enums for example, as I often search for common enum extension members I defined, but I need to find their usages on a specific enum, not all the usages on an open TEnum.

Also the existing implementation of current symbol only for Find Referrers does not work properly in that it finds too many items when Ctrl is used to limit results only to the current instance:

interface INameable { string Name { get; } }
abstract class NamedEntity  : INameable { 
  public string Name { get; set; }
}
class Country : NamedEntity { }
class Person : NamedEntity { }
class Program {
  void Main() {
    var c = new Country();
    c.Name = "Country";
    var p = new Person();
    p.Name = "John"; //<-- If I hover over Name here and choose Find Referrers with Match Symbol only, it will also find c.Name (the one on Country)
  }
}

It would be great if you could prioritize this, as this search is really indispensable for large solutions when you need to update/rename/refactor certain symbols and need to understand the specific places where they are used.

fitdev avatar Dec 08 '25 08:12 fitdev

There will be too many modifiers.

How about showing sub-command buttons at the menu? You can click the buttons to get the variant commands, as the following screenshot shows.

Image

On the above screenshot, the Copy Symbol command has two variants. Moving your mouse cursor onto them, you will get a tooltip telling you what they are for. No need to struggle with Ctrl and Shift any more, and there will not limit command variants to 3.

wmjordan avatar Dec 08 '25 13:12 wmjordan

That's awesome! Even better! I just thought modifiers would be easier to implement :)

fitdev avatar Dec 08 '25 16:12 fitdev

Deepseek helped me to implement that. You will see it in the next version.

wmjordan avatar Dec 09 '25 01:12 wmjordan

I find that some options are not mutually exclusive, and I turn those options into toggle buttons. You can check those buttons one by one for your needs.

The good aspect is that the toggle buttons are intuitive. The bad aspect is that it requires extra button clicks to launch the symbol search.

Image

wmjordan avatar Dec 09 '25 13:12 wmjordan

You are absolutely right - some options are not mutually exclusive. Your approach with toggle buttons is great! I love it! Extra click is a very small price to pay compared to what this gives you :)

fitdev avatar Dec 09 '25 14:12 fitdev

Please try the new beta.

wmjordan avatar Dec 10 '25 11:12 wmjordan

I will as soon as I figure out how to get back to VS after their yesterday's Insiders Release which prevents VS from opening any solution (so do not upgrade yet to the latest Insiders version).

fitdev avatar Dec 10 '25 12:12 fitdev

I really like the updated experience with toggle-able modifiers! Makes it very easy to use.

However as for the actual symbol search, the current version could not find for example the open generic extension member defined on TEnum when I selected Exact match. At the very least there should be 1 match - namely the one where I invovled the menu from. But in my case there would be several matches, yet none were found.

fitdev avatar Dec 10 '25 13:12 fitdev

the current version could not find for example the open generic extension member defined on TEnum when I selected Exact match

Please show me the code and let me see what's going on.

wmjordan avatar Dec 10 '25 13:12 wmjordan


public static class Extensions {

  public static string TextName<TEnum>(this TEnum it, string? fallback = null) where TEnum : unmanaged, Enum => "foo";
  
}

enum MyEnum {
 A,
 B,
}

class Program {

  void Main() {
    var a = MyEnum.A.TextName(); //<- When doing Find Referrer on `TextName`, and scoping to exact match, the result list is empty.
    var b = MyEnum.B.TextName();
    
  }

}

fitdev avatar Dec 10 '25 14:12 fitdev

The "Extract Match" option is intended to limit search results within the current type, excluding inherit or base types. The about situation is obviously a bug. I will fix it.

For your aforementioned requested limit on generics, maybe a new option "Match Type Argument" should be provided.

wmjordan avatar Dec 11 '25 08:12 wmjordan

For your aforementioned requested limit on generics, maybe a new option "Match Type Argument" should be provided.

Yes, that would probably be the most logical way to do it!

fitdev avatar Dec 11 '25 08:12 fitdev

p.Name = "John"; //<-- If I hover over Name here and choose Find Referrers with Match Symbol only, it will also find c.Name (the one on Country)

It is by design. Since the Name property is defined in the abstract class, and those sub-classes have not overridden it, the find reference of the Name property will return occurrences from both classes. If you change the Name property in the abstract class to virtual and have it overridden in the subclass, the Find References command can return references of the overridden property.

wmjordan avatar Dec 12 '25 08:12 wmjordan

There's a new beta. Please help test it.

wmjordan avatar Dec 12 '25 08:12 wmjordan

It is by design.

I see. But it will be very useful to add this feature as another scope. Perhaps implementation-wise it would simply filter the returned list by the type through which the member is accessed (which would be a derived type that inherited the member form its base).

fitdev avatar Dec 12 '25 14:12 fitdev

There's a new beta. Please help test it.

I really like the new modifiers! However I still cannot cover the case of finding all call sites of GetName on enum MyEnum. In other words, I need to find specific uses of open generic member, constrained to the current callsite's receiver type (and maybe even optionally just a specific member in case of enums)...


public static class Extensions {
  public static string GetName<TEnum>(this TEnum it) where TEnum: unmanaged, Enum => "foo";
}

public enum MyEnum {
  Bar, Baz
}

public enum MyEnum2 {
  A, B
}

public static class Program {
  public static void Main() {
    var e = MyEnum.Bar.GetName();
    var e2 = MyEnum2.A.GetName();
  }
}

fitdev avatar Dec 12 '25 20:12 fitdev

Check this option button, which will match the type arguments passed to the generic methods.

Image

wmjordan avatar Dec 13 '25 01:12 wmjordan

I don't see this option in the latest beta. The only 3 options I have are the last 3.

fitdev avatar Dec 13 '25 06:12 fitdev

Download the new beta then. The newer one has even more options, which allows you to limit result symbols in source code or referenced libraries.

wmjordan avatar Dec 13 '25 08:12 wmjordan

I still do not see the option in the latest beta:

Image

I should clarify perhaps that:

  • Extension Member TextName is defined in Base library project
  • Enum Type Sex is defined in a different library project
  • The caller (where the source is located) is in yet 3rd project that references both the Enum-containing project and the Extension-containing project.

Ideally this feature should work regardless of whether these are defined in the same or different projects - any combination should be supported. And this should apply to non-user code too, i.e. framework and Nuget library references.

fitdev avatar Dec 13 '25 10:12 fitdev

There's slight difference in the Super Quick Info. If you right click the large TextName<TEnum>, the symbol will be the generic class, so there's no option for it. If you click the large button on the left of TextName<TEnum> (see my screenshot above), you get access to the TextName<Sex>, so there'll be an option button.

wmjordan avatar Dec 14 '25 01:12 wmjordan

Oh I see. I did not realize there is a difference there (why is that btw?). It is very subtle and easy to miss! Perhaps this can be somehow made more obvious to the user, as I am sure many others won't discover it either.

I tested it and it works very nicely! I also really liked how you included a clickable generic type argument in the title bar of the results! Thank you!

fitdev avatar Dec 14 '25 06:12 fitdev

I hope you can also address the current / derived classes of the current only scope as well (exclude sibling and base classes) when the members are not overridden. I often need to search for these and the list can be very large especially for things like NamedEntityBase.Name type thing, where Name is only defined on the base, and is not overridden (regardless of whether it is virtual or not). It's very useful to find all references to such members only for a specific class (with/without its derived classes). If a Roslyn does not have an API for that, perhaps that can simply be implemented with the additional manual filter check - exclude all those results whose receiver type is not the type on which we invoked the command (and/or one of its derived classes).

fitdev avatar Dec 14 '25 07:12 fitdev

It is very subtle and easy to miss!

Yes, it is.

I have deliberately substituted the specific symbol with its original generic definition in the Quick Info, so the signature part on the Quick Info would not look very very long, when the type argument contains generic type, for instance, Dictionary<TKey, TValue> versus Dictionary<string, Dictionary<int, List<MySpecificClass>>>, etc.

Image Image

When a user right click the signature Dictionary<TKey, TValue>, he might be expecting that the symbol analysis command returns results about the generic type, not the specific one.

Nowadays we have more rooms to expand command options, maybe we can design a better experience for such type of scenario.

I'd love to hear your opinion.

wmjordan avatar Dec 14 '25 09:12 wmjordan

Please download the new beta, which attempts to make the signature context menu behave the same as the big button on the C# Quick Info.

I often need to search for these and the list can be very large especially for things like NamedEntityBase.Name type thing, where Name is only defined on the base, and is not overridden (regardless of whether it is virtual or not). It's very useful to find all references to such members only for a specific class (with/without its derived classes).

Well, it is somewhat reasonable. I will reconsider it.

wmjordan avatar Dec 14 '25 09:12 wmjordan

Yes, I very much like your elegant solution of not making the title too long with many generic parameters. I think the current solution works well. I was rather concerned a bit about discoverability of the fact that clicking on the title is not the same as clicking on the graphic symbol. But I am not sure what the right solution would be. I will give the new beta a try and let you know.

fitdev avatar Dec 14 '25 14:12 fitdev

Well, it is somewhat reasonable. I will reconsider it.

Thank you. I hope you implement it, as otherwise there are sometimes 100s of search results to go through, when in fact you only need a few of them for a particular class.

fitdev avatar Dec 14 '25 14:12 fitdev