Should intellisense do what you want, or do what’s correct?


Ok, that could be a terribly misleading title, but hopefully some explanation will help clarify things.  I just got a bug assigned to me complaining about the following behavior.  Say you have the following:


Yes master!


Looks good right?  Well, not quite.  Technically, the list of valid things after an enum name plus a dot are all the other things we would have found while resolving the static members in the inheritance chain.  So we could have shown you:



        public static string Format(Type enumType, object value, string format);


        public static string GetName(Type enumType, object value);


        public static string[] GetNames(Type enumType);


        public static Type GetUnderlyingType(Type enumType);


        public static Array GetValues(Type enumType);


        public static bool IsDefined(Type enumType, object value);


        public static object Parse(Type enumType, string value);


        


In this case we didn’t because (in one of the extremely rare cases where we filter out elements of a list) we felt that it would actually be a detriment to show you things that would be legal to use in that circumstance.  We normally have a policy for showing everything valid because we believe that intellisense shouldn’t interfere with typing.  I.e. you should be able to type in whatever you wanted to get verbatim and not have the intellisense system screw you up.  Of course, we already violate this when you hit <dot> and try to access a member you haven’t defined yet.  We err in that case with the presumption that accessing a pre-existing element is far more likely than accessing one you’re about to create.  Of course, methodologies like TDD have forced us to rethink this and realize that there is indeed a class of users for which this behavior can be undesirable in a large percentage of cases. 


In this specific case the reasoning was two part.  First, if you’re accessing a specific enum type (like color) your almost certainly want to access one of its elements (if you think this assumption is wrong let me know!).  Second, the elements we are masking are accessible if you type out “Enum<dot>”, so it’s not horrible to remove them since they are accessible.


We often times get a suggestion that when a user types “throw new<space>” we should automatically pop up the list prefiltered to only things that are exceptions.  The problem with that is that really after a “throw” any expression is legal as long as it evaluates to a type that derives from exception.  While rare, it happens that people do interesting things like throwing preexisting exceptions that they’ve retreived from some storage or have created in novel ways.  Unlike the Enum case above, there is no way to type the expression without having our filtering interfere.


I mentioned that I wasn’t sure if the title of this post was really appropriate.  It implies an either/or relationship between the two ideas.  It’s quite possible that there are those out there (like the person who opened the bug) that what they want is for intellisense not to try to guess what you’re doing and filter but to always show what it knows to be valid to the best of it’s abilities.  However, I do believe that there are a lot of people out there who would like intellisense to be smarter, and up to know have not realized why it is we don’t do certain “smart filterings” based on what you’ve typed.  This is an area I really want to address with the next version and I’d like to do so in a way that benefits both those who want the development advantages smart filtering could provide and does not hinder those who don’t want intellisense second guessing them and making their life harder.


Have you felt at times “why can’t intellisense be smarter right now?” or “why is it showing my so much stuff I don’t care about?” then let us now so that when we work on a system that tries to be better than the current one we can make sure we’re addressing the cases that you’ve brought up.


Edit: System.Enum and System.Exception have been discussed as areas for smarter filtering of completion lists.  Are there other areas where you see this being helpful.  It should be noted that for VS2005 we’ve implemented filtering of completion lists for attribute declarations down to types that derive from System.Attribute.  For catch blocks we filter down to types that derive from Exception; and we’re thinking about introducing a filter down to types that derive from Delegate when you’re declaring an event’s return type.


Comments (42)

  1. Dr Pizza says:

    How does IntelliSense "interfere"? It doesn’t restrict what one can type; you can still type things not on the list. How about if, for instance, it filtered by default, but if you typed something in-scope but filtered, it broadened the filter? So if I typed Colors.Get the filter would broaden to show more than just the enumerated values?

  2. Dr Pizza: Very interesting idea. Kind of the reverse of what we were considering as a model as well. Namely that we would start with the most inclusive filter but as you typed we would trim down the list.

    That gets very interesting because imagine if you had something like (making this up onthe spot).

    class Foo

    {

    int WindowAccessibility;

    int MenuAccessibility;

    //_tons_ of other stuff

    }

    Foo foo = new Foo();

    foo<dot>

    Now you have a list with a ton of stuff in it. Say you type "accessibility" that no longer means "find me the thing in the list that starts with accessibility" it instead means "show me all the things related to what I’ve typed. Prefereably prefix matches first, then substring matches, then close spelling matches, etc."

    That might be very interesting, especially in a model where you’re searching for something but dont’ quite remember the name. "Was is WindowAccessibility, or AccessibilityWindow?" etc.

    I haven’t run into this problem, but I’ve heard that people who do a lot of forms work do. That’s why I’d like to hear if there are specific areas where the current completion sets get very unweildy and difficult to deal with.

  3. IMHO, something like "pressing the dot will display FilteredSense ™, pressing and holding the dot (even something like Shift+Ctrl+.) will display free form" would be better than writing "Get".

  4. Jens Peter Grosen says:

    You have to have two different types of completions as the IDE cannot possibly understand what you want to do.

    If you look at e.g. the ReSharper plugin, it has both normal intellisence (ctrl+space – that would show Format and parse) and correct intellisence (ctrl+shift+space – that shows Blue, Green, Red).

  5. Jens: This was a model I’ve been thinking about. However, I’m not (totally) convinced that it’s the only way to solve the problem πŸ™‚

    Rather than the two invoke methods, I was thinking about doing what you said by having a completion list with two tabs on it: "all" and "filtered". You could get between them by hitting left/right (or maybe ctrl-left/ctrl-right etc.)

    Benefits being it fits both models of users _and_ it’s extremely discoverable.

  6. Mark Allan says:

    With the caveat up front that you should be able to configure the default behaviour, I think it would be useful if the initial popup showed the options that you’ll be after in 95% of cases, but with the ability to show the lesser-used options on request (like personalised menus). So Colours. would initially show

    Blue

    Green

    Red

    (v)

    but would give you the other possibilities if you clicked on the arrow at the bottom, or hit Alt + ‘+’, or typed anything that took you out of the scope of the current completion list (e.g. "F" for Format).

    The idea of matching on substrings is interesting, but I think it might make more sense to have it in the Object Browser – it feels to me like it’s straying outside what Intellisense should be trying to do for you (though if you can think of a logical way of doing it then you might want to make it a configurable behaviour).

  7. Mark: "it’s straying outside what Intellisense should be trying to do for you"

    Man. I which we could define what it was the intellisense was trying to do for you.

    If I had to try, I’d say that our mandate is that it do whatever possible to improve developer productivity. πŸ™‚

  8. Luc Cluitmans says:

    Quote: "We often times get a suggestion that when a user types β€œthrow new<space>” we should automatically pop up the list prefiltered to only things that are exceptions."

    Eh, what’s wrong with that? The counterexample you give would not involve the ‘new’ keyword (it would be ‘throw GetMyPreformattedException()’ or something like that). Your argument is correct after ‘throw<space>’, but not after ‘throw new<space>’ …

  9. Luc: After "throw new" you are not required to write a type that extends from exception. you could (And people do) do something like:

    throw new MyExceptionAggregator().CurrentApplicationException;

    where MyExceptionAggregator doesn’t derive from Exception.

  10. Luc Cluitmans says:

    Ok – realized that too, about one minute after I made my message… (and comments are not editable)

    But does that case occur often enough to warrant not filtering the exception list? As was mentioned before, having the completion list is not enforcing you to use it, isn’t it?

    I have difficulties thinking of scenarios where one actually would want to use the example you give. I can see someone using a field, a singleton or some static methods to do some more complex exception initialization, for instance for localization purposes. But creating a *new* MyExceptionAgregator to generate a new exception (or retrieve an existing one) looks a bit strange…

  11. Stuart says:

    A slight tangent which your post reminded me of. You’re probably not the person who could do anything about this, but maybe you know someone in the C# language group who would be able to. You pointed out that technically it’s possible to type Colors.Parse(…) and the intellisense filters this out. My first reaction was "wow, Colors.Parse actually works? I always wanted that! How dare intellisense have stopped me from realizing that?".

    After I started to compose a post explaining that I really wanted intellisense to allow me to see the Parse method on enum types, I realized what you really meant. Colors.Parse isn’t the useful method I would want it to be, but simply a (useless) alias for Enum.Parse, which means that to get the behavior I want, I still have to type (Colors)Colors.Parse(typeof(Colors),str) (or something, I forget the exact parameter order). Why oh why doesn’t the enum keyword automatically generate a static Parse method on every enum type so I could just type Colors.Parse(str)?

  12. Stuart says:

    Oh, and back on the subject you asked about – perhaps intellisense could keep track of the other things that are valid, and if it notices you starting to type something that’s on the full list but not the displayed list, show the other items (in a different color, probably). The only case that’s problematic if you do that is if you have an enum containing, say, a ParseFoo value. In that case, perhaps if you actually type out MyEnumType.Parse in full, intellisense shouldn’t autocomplete it to ParseFoo, even though it didn’t actually display Parse in the list because the thing you were typing was possibly in the list.

    I’m sure you can think of lots of variations on this behavior to experiment with, anyway. Another possibility would be to display the filtered list as soon as you hit ".", but after that show the full list (with the probably-useless ones in a different color) as long as what you’re typing matches any of the otherwise-hidden entries.

  13. Stuart: "Why oh why doesn’t the enum keyword automatically generate a static Parse method on every enum type so I could just type Colors.Parse(str)? "

    Excellent question. Excellent, excellent question. Wow. Hrmm. Well, I’ll try to figure that out ASAP πŸ™‚

  14. Luc: Yup. I agree. It’s a very corner case scenario, but it does occur. That’s one of the fundamental isssues here. Should we be "right" or "correct" πŸ˜‰

  15. Stuart: Interesting ideas and we will defintely be playing with a lot of them in the future. I’m looking for ones that definitely help with deiscoverability though. i.e. showing additional members if you type some correct substring could potentially confuse teh hell out of people that don’t know this type 100% backwards and forwards.

  16. I think you should display a full list, with the possibility of filtering coming later.

    You could use ctrl-m to filter in methods, ctrl-p for properties, ctrl-e for events, and ctrl-. to initiate free-form search (looking for the searhs tring in the name, paramaters, documentation, etc etc)

  17. Stuart says:

    More thoughts: it actually occurs to me that this problem isn’t specific to enums. I always wondered why any valid typename always gave me "Equals" and "ReferenceEquals" in the completion list, and now I understand why. On the one hand this is allows the useful trick of checking whether you typed in a valid typename by hitting <dot> and seeing whether the "Equals" and "ReferenceEquals" show up or not, but on the other hand, I’ve never encountered a case where you might EVER want to call Equals or ReferenceEquals through a type other than Object. (Actually, I just realized that you never even need a typename at all for these, because they’re always directly in scope by inheritance).

    I’d actually like to see a warning if you call a static method explicitly through a type other than the one that declares it. This seems similar to the "object == string" case which the compiler already warns about – it’s legal C# but unlikely to do what you expect.

    public class Foo {

    public static void DoSomething() {…}

    }

    public class Bar : Foo {

    public static void ARandomMethod() {

    DoSomething(); // No warning here, it’s in scope by inheritance

    Foo.DoSomething(); // No warning here, it’s actually declared in Foo

    Bar.DoSomething(); // Warning here, it’s not declared in Bar

    }

    }

    Needless to say, if you’re going to give a compiler warning on it, you shouldn’t show it in intellisense. If you do decide to do this, please add *something* to show up on a valid typename with no static members, eg a list that just contains an unselectable "<no static members>" entry or something. That’s probably less confusing than showing the useless "Equals" and "ReferenceEquals" entries anyway, and it still allows you to tell that the type is valid.

    (Damn, it’s hard to not rant about all my intellisense gripes when I have someone on the team writing it… I can’t resist adding this one other intellisense complaint:

    If I have a type Foo and a variable or property Foo both in scope at the same time, the C# compiler is perfectly capable of disambiguating in almost any case, but intellisense completely gives up the ghost and won’t give me any help for any variable of type Foo at all. Haven’t tested this in 2005 yet, so maybe you already fixed it, but if not, that would be my #1 intellisense request. This pattern shows up pretty frequently in my code – I often have things like:

    private Customer customer;

    public Customer Customer {get {return customer;}}

    In a class with such a declaration, I get no intellisense on Customer, customer, or any other variables of type Customer. That sucks. End of rant.

    )

  18. Stuart: The whole rule "don’t show me static members unless I’m expliciting accessing the type which they are declared on" seems like a great heuristic to add to the list of things that generate the filtered list!

  19. Stuart: Fixed for 2005.

    If you have problems then *please* file bugs on them at http://msdn.microsoft.com/ProductFeedback

    I cannot express how important this is. We do not catch all poassible variations in code that can arise and I do not want you griping for the next few years because of some glitch in intellisense that bugs you πŸ™

    (but also rant. Rants are good. they make us understand the pain we’re causing people and how badly those issues need to get fixed!!)

  20. While we’re on the IDE behaviour, here’s another one for ASP.NET with VB.NET on 2003 IDE(didn’t tested on 2005, though):

    CType(ViewState("ActiveVersion"), Long)

    CType(viewstate("ActiveVersion"), Long)

    CType(viEWStAte("ActiveVersion"), Long)

    All three are valid (by design of the language) but IDE doesn’t automaticly convert the last two into "ViewState". When you type "viewstate(", it pops up the usual yellow box with ViewState() As System.Web… so IntelliSense discovers it very well. And this behaviour is specific only to ViewState, AFAIK.

    I don’t think that it’s a big deal, but FxCop do.

  21. Sorry Gokhan: C# guy over here. Not even sure what to do about that. I’d file a bug if I were you so it will get fixed πŸ™‚

  22. I think it is better to start with the constrained list and have an easy way to expand it. This goes for both Enum’s and Exceptions.

  23. Michael: What would that easy way be? How would you make it discoverable to the user?

  24. Thomas Eyde says:

    I would like an option to filter on what makes sense in my current context, including local variables and parameters. If I am about to assign something to a string, please show me things which evaluates to string.

    There should be a way to switch between modes, like ctrl + shift and ctrl + alt + shift.

    What if intellisense could learn? So if I type

    throw new MyExceptionFactory().GetMostLikelyException();

    then intellisense adds MyExceptionFactory to the list as long as it’s available.

    I would like intellisense or autocompletion for keywords as well:

    pub vir void Execute() <ctrl + enter>

    gives

    public virtual void Execute()

    {

    // <– cursor here!

    }

    Autocompletion would generally be nice. Type:

    string s = "abc <enter>

    and the editor should complete it to:

    string s = "abc";

    // <– cursor here!

  25. Thomas: I like these ideas πŸ™‚

    If it helps, we’ve added keywords to intellisense and autocomplete.

    Doing a lot better on finishing things up for you is something I’d really like to work on as well.

    Learning is a very interesting proposition. i’m not sure if it would do well enough, but it’s definitely worth exploring.

  26. AT says:

    Additional suggested method for Enum<T>

    public static bool TryParse<T>(string stringValue, out T enumValue);

    It was reported as 320417672 at 6/19/2004 and "has been forwarded to the appropriate group" ;o)

    I agree with most of people – "throw new" must list only exception constructors, but "throw " must list keyword "new" and others methods/variables suitable as exceptions.

    Both lists can behaive as Most Recently Used, after first failure to predit intellisense text – values must be added to list.

    As second step – it must be possible to press Delete or Ctrl+Delete to remove incorrectly added item from IntelliSence list.

  27. AT: Unfortunately, you cannot constrain a type variable down to the Enum type. I’m going to see what Anders has to say about that restriction

  28. AT says:

    Cyrus: Yea. It will be nice to hear why "where T:struct" works but "where T:enum" does not.

    But even using current "where T:struct" we can restrict type to value-type.

    The biggest benefit of generics will be compile-time prevention of errors like

    Y y = (Y) Enum.Parse(typeof(X),"Value"); // X vs. Y type

    Currently this kind of errors will be undetected even at runtime !!!

    Take a look on my helper class for type-safe Enums πŸ˜‰

    http://24.odessa.ua/CSharp/Enums.html

    public static class Enums<T> where T : struct

    {

    public static bool TryParse(string stringVal, out T enumValue)

    {

    // TODO: Provide system support for TryParse

    if (Enum.IsDefined(typeof(T), stringVal))

    {

    enumValue = (T)Enum.Parse(typeof(T), stringVal);

    return true;

    }

    enumValue = default(T);

    return false;

    }

    public static T ToObject(long value)

    {

    return (T)Enum.ToObject(typeof(T), value);

    }

    public static string[] GetNames()

    {

    return Enum.GetNames(typeof(T));

    }

    public static T[] GetValues()

    {

    return (T[])Enum.GetValues(typeof(T));

    }

    }

  29. Darren Oakey says:

    Cyrus :

    my answer would be a bit "what you want" πŸ™‚

    As you pointed out above – intellisense is there to aid productivity. I think I personally would be a lot more productive if I was looking at a very restricted list. I’d prefer something aimed at helping me 90% of the time, rather than something aimed at helping me 100% of the time. I’m quite happy to type something that is not in the list, than have too many things in the list.

    For instance: Equals and GetType. I do use them. Occasionally. GetHashCode I’ve never explicitly called in my life. All three of them, however, I don’t want in my list. The few times I will call them, I’m quite happy to type them in, but having three more items in every single list I ever see I don’t want! In some ways I’m almost tempted to suggest not showing ANY inherited members – it would lead to FAR better code πŸ™‚ – but I’ll acknowledge that most people would disagree with me πŸ™‚

    With enums, I’m very unlikely to call enum.anything – only in a small percentage of cases – and so I’d rather only see Color.[Red|Green|Blue] – and have to go and hunt for the rest.

    I like the idea of two lists, filtered and unfiltered – but don’t know about the two tabs – I’d prefer my eyes to see as little as possible when I’m looking for what I’m expecting. What about the simple option of this: on the first control-space or other intellisense causing event, show the superfiltered list. Control-space again expands that? It’s would be very easy to keep typing without losing context, or your fingers having to come off the home keys, and even though it’s less discoverable, only has to be learnt once and used many πŸ™‚

    As for the list filtering – add one vote for the exception filtering! I can’t think of anything else that can come after throw new … Similarly for attributes – only showing attributes after [… things that take delegates only showing functions, things that take types only showing types and so on.

    Also, a weird request – but we have basically chucked enum’s.. Enums would be FANTASTIC if you could add functionality to them.

    … but you can’t ….

    and I HATE

    > switch (x)

    > case Color.Red: DoForRed(); break;

    > case Color.Blue: DoForBlue(); break;

    – I think it’s horrible. I much prefer

    > x.DoSomething();

    So, in reality here I don’t allow anyone to use enum (or switch :))… ALL our "enums" are actually defined like:

    > public abstract class Color

    > … [any functionality]

    > private class RedColor : Color {…}

    > private class BlueColor : Color {…}

    >

    > public static readonly Color Red = new RedColor();

    > public static readonly Color Blue = new BlueColor();

    So.. ideally those static functions would come up the same way enums do… πŸ™‚ (or, much better – allow an enum to implement an interface! (which isn’t as dumb as it sounds – because even though the enum is a number – it can still look up a static instance of an object, and if that plumbing was all transparent, we could make some much more elegant programs)

    eg:

    > enum ConfiguredDataSourceType : ProvidesDataSource

    > {

    > Database

    > {

    > DataSource ProvidesDataSource.Source() {return new DatabaseSource();}

    > }

    > XMLFile

    > {

    > DataSource ProvidesDataSource.Source() {return new XMLFileSource();}

    > }

    > }

    of course that’s just an example I quickly thought of – please don’t come back and say "why wouldn’t you just store the class name instead of some arbitrary "type code" and dynamically create it" – because of course I would.

    PS – speaking of intellisense… it would be really nice to have the facility to put in a config file "Never show me". There’s exactly one class in the system that REALLY gets to me. We use the _ scoping prefix for class level variables – so we hit _ control space to get up our local scope. Which works great, except for the one @$!#$ class "_appdomain" which feels the need to jump into our lists πŸ™

  30. Jeff Casimir says:

    I think I read through everything, but forgive me if this has already been settled. I am way tired…

    Anyway, isn’t this a similar problem to fonts in MS Word? Many users have dozens or hundreds of fonts. But, they are most likely to use thier most recent.

    On your Enum example, what about a single popup that looks something like this:

    Blue

    Green

    Red

    —(Horizontal Rule)—

    Whatever

    Other

    Possibilities

    Up

    The

    Tree

    Given that the first group is going to be used most of the time, it should text match only among that group until a match cannot be found, then match among the full list. Anyone who is using the less common case can probably get two (or three, etc) letters on thier own, or bother to scroll through the list with thier mouse.

    It seems a lot simpler than hotkeys, tabs, etc.

  31. AT says:

    Okey-okey … How about another trade-off?

    Make it possible to use to select how IntelliSence will beehive in IDE settings?

    BTW, Do VSIP allows adding own IntelliSence helpers?

    Options can be:

    a) Show everything possible sorted alphabetically (current, default)

    b) Show most common used and all others using Horizontal splitter. Some keyboard shortcuts must be provided to move items between those two lists.

    c) Show tabbed interfaces. Again – shortcuts can be used to move items from one panel to another. Option must be given to save IntelliSence Lists on Project/User/Machine basis.

    d) Show tabbed interface with powerful search and documentation window showed for each member (ala Dynamic Help – but inside IntelliSence List). Make it possible to search inside help text

    e) Most crazy idea. Use Google search API to fetch results for IntelliSence drop-down list. User will be prompted to enter a few keywords and VS.IDE will perform Google search on code snippets to complete user idea.

    f) In addition to simply next member – IntelliSence can provide a way to complete entire block. Like a code snippets – but with automatic discovery. It must be possible to teach IDE on common techniques. Using clustering IDE must detect common patterns and you can give them some names/description.

    As result IDE must automatically recognize start of source code for common patterns and provide suggestions on code blocks. Implement Naive Bayes learning algorithm to classify source text based on name/type/visibility and initial source code part.

    For example "public MyObject GetInstance() {<BANG>" must provide singleton patterns list in IntelliSence suggestions.

    "public int CastMagicSpell(<BANG>" must provide list of possible overrides to specify default values for parameters. Selecting one of them will create source code for method and documentation for this override.

    No needs to study all crazy names of expansions.

    P.S> There are infinite number of ideas waiting for somebody implement them.

  32. Jeff: That’s a great suggestion, and it’s definitely one we’ll be trying, but there are several issues with it that we’re aware of already.

    The font menu has very different properties than what we’re looking for. For example, it’s intended to be mostly mouse driven. It’s difficult ot navigate with the kwyboard because it’s not clear how matching works. If you have:

    FooBar



    ——–



    Foo

    And you type: "Fo" where should you be? etc. etc.

    However, a grouped list that filtered as you typed would get around these issues and could be very cool!

    Using a MostRecentlyUsed list of elements along with a heuristic for smart ways to group, we could potentially help you out a lot.

  33. AT: There are no beehives in VS! We got rid of all of them.

    I like the way you think πŸ™‚

    Looking for a job? πŸ˜€

  34. Jeff Casimir says:

    In the example…

    FooBar



    ——–



    Foo

    If you type Fo, you should remain in the top list. I really think the lower list would be ok to need a little mouse interaction if you need one of the more obscure choices but can’t manage to type it uniquely. The text should match against the top section until it can’t find a match. Also, if the non-matching substring is edited (if you have a typo that bounces you to the full list then delete it to mini-list valid substring) it should lock back into the mini-list section. I think.

  35. Jeff: That interaction model really worries me as it ends up punishing someone typeing something that is very reasonble. It forces them to type out fully something they used to be able to type just a substring of.

    That said, I think i need to do a better job realizing that there might not be "one right way to do this" and having options to change behaviors might work out best here. Those that then find this beneficial can have this behavior and those that like the current behavior can stick with it πŸ™‚

  36. I agree with AT! Why don’t we have an IntelliSense configuration dialog, like the C# formatting one (i.e., a million options)? πŸ™‚ If that’s possible on your end (resources), it’d be great. Most people are somewhat professional developers and could figure out a few IntelliSense options to make it work just like they want to.

  37. Penis says:

    Download 7 penis enlargement videos. User ratings & reviews of 55 penis enlargement pills.

  38. Penis enlargement pills, patches, extension devices and exercises reviewed by over 500 users. Find out if penis enlargement products actually work.