Partager via


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

  • Anonymous
    August 01, 2004
    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?

  • Anonymous
    August 01, 2004
    The comment has been removed

  • Anonymous
    August 01, 2004
    IMHO, something like "pressing the dot will display FilteredSense (tm), pressing and holding the dot (even something like Shift+Ctrl+.) will display free form" would be better than writing "Get".

  • Anonymous
    August 01, 2004
    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).

  • Anonymous
    August 01, 2004
    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.

  • Anonymous
    August 01, 2004
    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).

  • Anonymous
    August 01, 2004
    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. :-)

  • Anonymous
    August 01, 2004
    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>' ...

  • Anonymous
    August 01, 2004
    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.

  • Anonymous
    August 01, 2004
    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...

  • Anonymous
    August 01, 2004
    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)?

  • Anonymous
    August 01, 2004
    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.

  • Anonymous
    August 01, 2004
    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 :-)

  • Anonymous
    August 01, 2004
    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" ;-)

  • Anonymous
    August 01, 2004
    The comment has been removed

  • Anonymous
    August 01, 2004
    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)

  • Anonymous
    August 01, 2004
    The comment has been removed

  • Anonymous
    August 01, 2004
    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!

  • Anonymous
    August 01, 2004
    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!!)

  • Anonymous
    August 01, 2004
    The comment has been removed

  • Anonymous
    August 01, 2004
    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 :-)

  • Anonymous
    August 02, 2004
    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.

  • Anonymous
    August 02, 2004
    Michael: What would that easy way be? How would you make it discoverable to the user?

  • Anonymous
    August 02, 2004
    The comment has been removed

  • Anonymous
    August 02, 2004
    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.

  • Anonymous
    August 02, 2004
    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.

  • Anonymous
    August 02, 2004
    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

  • Anonymous
    August 02, 2004
    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));
    }
    }

  • Anonymous
    August 02, 2004
    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 :(

  • Anonymous
    August 02, 2004
    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.

  • Anonymous
    August 03, 2004
    The comment has been removed

  • Anonymous
    August 03, 2004
    The comment has been removed

  • Anonymous
    August 03, 2004
    AT: There are no beehives in VS! We got rid of all of them.

    I like the way you think :-)

    Looking for a job? :-D

  • Anonymous
    August 03, 2004
    The comment has been removed

  • Anonymous
    August 03, 2004
    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 :-)

  • Anonymous
    August 04, 2004
    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.

  • Anonymous
    February 07, 2006
    The comment has been removed

  • Anonymous
    March 11, 2006
    The comment has been removed

  • Anonymous
    June 09, 2009
    PingBack from http://quickdietsite.info/story.php?id=4687

  • Anonymous
    June 16, 2009
    PingBack from http://topalternativedating.info/story.php?id=8007