Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Visual Studio JavaScript Intellisense Revisited

4.85/5 (14 votes)
22 Apr 2010CPOL9 min read 1  
Or: “What I hope is not just ‘Yet Another Visual Studio JavaScript Intellisense Walkthrough’”

Or: “What I hope is not just ‘Yet Another Visual Studio JavaScript Intellisense Walkthrough’”.

Author note: If this content seems dated, it is. When I started this blog last year, I began by resurrecting a monster JavaScript documentation project that had been dormant for a year. Then I got busy and dropped it again. So here I am picking it back up again to provide some motivation for actually finishing a POJO documentation generator using XML doc comments and SCHB. While VS JScript Intellisense is nothing new, I think that there are topics covered that I have not seen in other posts.

UPDATE: JS <s>enum</s> intellisense is broken in VS10 RC.  I will update when resolved.

UPDATE: 04-22-2010 - Ok, am in the process of exploring VS10 JS intellisense support and will update shortly. 

The VS08 JavaScript intellisense engine is driven, in part, by two separate but related types of artifacts; Ajax specific object properties and XML doc comments.

XML doc comments are a fairly well known quantity these days, so let’s focus first on the Ajax specific properties.

MsAjax has three primary methods that comprise the typing system. These are:

  • Type.registerNamespace()
  • Type.registerClass()
  • Type.registerEnum()

When these methods are called, a lot of moving parts are involved in the context of MsAjax but in the scope of this discussion, we are only interested in what the object looks like when it comes back.

If you compare the properties of an object before and after it is passed to one of the register methods, you will notice a handful of new properties, all prefixed with double underscore.

The three we are most interested in are __namespace, __class and __enum

These properties have special meaning to Visual Studio and change the visual cues that are presented in the intellisense window.

Consider Listing 1 and the accompanying image. This represents the typical view a developer has when coding against some POJO code.

Listing 1 - POJO

JavaScript
// declare a 'namespace'
var MyNamespace = {};

// declare a 'class'
MyClass = function() {
}

// declare an 'enum'
MyEnum = function() { }
MyEnum.prototype = { None: 0, One: 1, Two: 2 };
MyEnum.None = 0;
MyEnum.One = 1;
MyEnum.Two = 2;

Now, if we simply decorate each object with a boolean property that describes the type, Visual Studio will adjust its visual cues accordingly. These properties are predictably named __namespace, __class and __enum. As you can see, in the case of enum, there is a little more happening to provide a fairly accurate implementation of an enum in JavaScript and we will cover that in some detail.

Consider Listing 2 and the accompanying image. While this represents no functional change to the code and the value of these visual cues is totally subjective, I have found that in large object models, these visual cues have definite value and I implement them whenever it makes sense to do so.

Listing 2 - AJAX

JavaScript
// declare a 'namespace'
var MyNamespace = {};
MyNamespace.__namespace = true;

// declare a 'class'
MyClass = function() {
}

MyClass.__class = true;

// declare an 'enum'
MyEnum = function() { }
MyEnum.prototype = { None: 0, One: 1, Two: 2 };
MyEnum.None = 0;
MyEnum.One = 1;
MyEnum.Two = 2;
MyEnum.__enum = true;

JavaScript Enums and Intellisense

We are now going to start to blur the lines between eyecandy and true value add.

Enums are a data structure that provide both human friendly labels and type-safe values to a variable and are widely used in most programming languages. The virtues and benefits of the proper use of enums should be a given to any programmer and will not be further discussed here.

Over the years, I have attempted and encountered many techniques at implementing enums in JavaScript to varying degrees of success. Each carried its own smell/value ratio but in no case did the implementation lead to intellisense and code completion.

The technique I finally settled with was to specify the Name/Value pairs in both the prototype and on the function itself as illustrated in Listings 6 and 7. The results were functional but unspectacular from an intellisense perspective. Peep this…

It is not obvious from the image but this is me dotting my enum and getting nothing.

So, it may be understandable why, when I initially started playing with msajax and I actually saw JavaScript enum intellisense and code completion with pretty enum-y visual cues, I went diving into MicrosoftAjax.js.

When I did find the code that created the structure, registerEnum, I was both surprised and confused. What I found was a technique functionally equivalent to what I was doing but had no idea as to why my enums were flat with no code completion for the values.

It wasn’t until I read through the entire MicrosoftAjax.js file that I encountered the only two enums declared, "Sys.UI.MouseButton" and "Sys.UI.Key" that it made sense.

The code included C# style XML doc comments inside the function that described the fields. I was definitely interested in this in general but at the time I was anxious to apply it to my own enums and see the results.

Listing 3 – JavaScript XML Doc Comments on Enums

JavaScript
// declare an 'enum'
MyEnum = function() {
/// <field name="None" type="Number" integer="true" static="true"/>
/// <field name="One" type="Number" integer="true" static="true"/>
/// <field name="Two" type="Number" integer="true" static="true"/>
}
MyEnum.prototype = { None: 0, One: 1, Two: 2 };
MyEnum.None = 0;
MyEnum.One = 1;
MyEnum.Two = 2;
MyEnum.__enum = true;

Now that is purty and, unlike the previous eyecandy, does represent a value add to the Visual Studio JavaScript coding story.

So, with much excitement, I re-reviewed the Ajax code and sussed out a pretty sound understanding of how to provide visual cues and intellisense for properties and events.

  • Property accessors: prototype functions prefixed with ‘get_’ and ‘set_
  • Event handler accessors: prototype methods prefixed with ‘add_’ and ‘remove_

Listing 4 – Fully documented model

C#
MyNamespace = {
    MyEnum: function() {
        /// <summary>enum summary</summary>
        /// <field name="None" type="Number" integer="true" 
        /// static="true">enum field summary</field>
        /// <field name="One" type="Number" integer="true" 
        /// static="true">enum field summary</field>
        /// <field name="Two" type="Number" integer="true" 
        /// static="true">enum field summary</field>
        throw new Error();
    },

    MyClass: function(publicFieldValue) {
        /// <summary>class summary</summary>
        /// <param name="publicFieldValue" type="String">ctor param summary</param>
        /// <field name="publicField" type="String">public field summary</field>
        /// <returns type="MyNamespace.MyClass">return type summary</returns>

        if (this instanceof MyNamespace.MyClass) {

            this.publicField = publicFieldValue; 	// public fields can be exposed 
						// and documented
            this._propertyBacker = publicFieldValue; 	// prefix underscore 
						// usually hides 
						// 'private' fields

            this._priveledgedMethod = function(param) {
                // comments on privileged methods are ignored by visual studio
                // <summary>priveledgedMethod summary</summary>
                // <param name="param" type="MyNamespace.MyEnum">
                // priveledgedMethod method param summary</param>
                // <returns type="Array">priveledgedMethod return type summary</returns>

            }
        } else {
            return new MyNamespace.MyClass(properties);
        }
    }
    , __namespace: true
};


MyNamespace.MyEnum.prototype = { None: 0, One: 1, Two: 2 };
MyNamespace.MyEnum.None = 0;
MyNamespace.MyEnum.One = 1;
MyNamespace.MyEnum.Two = 2;
MyNamespace.MyEnum.__enum = true;

MyNamespace.MyClass.prototype = {
    publicMethod: function(param) {
        /// <summary>publicMethodmethod summary</summary>
        /// <param name="param" type="MyNamespace.MyEnum">
        /// publicMethod param summary</param>
        /// <returns type="Array">publicMethod return type summary</returns>
        return this._privelegdedMethod(param);
    },
    get_propertyBacker: function() {
        /// <summary>property getter summary : proscribed</summary>
        /// <value type="String">property value summary</value>
        return this._propertyBacker;
    },
    set_propertyBacker: function(value) {
        /// <summary>property setter summary : proscribed</summary>
        /// <param name="value" type="String">property setter param summary : 
        /// implicitly proscribed</param>
        this._propertyBacker = value;
    },
    add_eventHandler: function(handler) {
        /// <summary>event handler add summary</summary>
        /// <param name="handler" type="Function">event handler add param summary: 
        /// not mentioned</param>
        this._eventHandler = handler;
    },
    remove_eventHandler: function(handler) {
        /// <summary>event handler remove summary : implicitly proscribed</summary>
        /// <param name="handler" type="Function">
        /// event handler remove param summary : implicitly proscribed</param>
        this._eventHandler = handler;
    }

}
MyNamespace.MyClass.__class = true;

When I found the typed <returns/> tag in MsAjax, I realized that this was the true gem. Of the various facets of Visual Studio JavaScript Intellisense, Type Inference alone is enough to justify XML doc comments. Type Inference via the <returns/> tag is what makes possible, amongst many other things, the elegantly fluent jQuery API when used in Visual Studio.

Here is Listing 4 in action

Sometime later, I encountered a blog post by Bertrand LeRoy that provided a complete specification of the format for JavaScript Intellisense comments. Other references include the Gu’s post on the subject, a condensed version on StackOverflow and, of course, there is also MSDN content but it is of limited value, in my opinion.

I will let you get the ‘official’ lowdown from those posts. What follows are my results and observations.

It came as some surprise to me that some of the XML-doc structures I had discovered either by extrapolating what I read in MicrosoftAjax.js or as a result of trial and error that work so well were either not mentioned in any of the official sources listed above or explicitly advised against with no rationale or justification, leaving me in a somewhat untenable position. I don't live to question authority but when something that seems to be working for me is proscribed without explanation, I tend to resist.

Amongst the ‘rules’ I found myself breaking yet still providing valuable intellisense are:

  • summary tags on property getters
  • summary and param tags on property setters
  • param tags on event handler adders
  • summary and param tags on event handler removers

When I put on my ‘let’s be reasonable and try to infer these apparently unknowable reasons not to do this” hat, the only thing I can come up with, since Visual Studio seems to be quite content, is that perhaps documentation generators may be confused by them. When I consider that the only jscript XML doc comment generator that I know of is Betrand’s AjaxDoc and it consumes object models built with MsAjax in the context of MsAjax, I find myself not overly concerned. A few years ago, I made an initial stab at a POJO doc generator using XML doc comments and will most likely revive it once VS10 goes gold.

As a side note: After years of dormancy, I have noticed some recent activity in AjaxDoc and can only assume it is in preparation for the latest version of Ajax and Visual Studio. This is a good thing, I would like to see the benefit of the past few years experience manifest in improvements to that project. It has great potential.

Buggy Behavior of the Visual Studio Jscript Intellisense Provider

After having gone to all the effort to emplace XML doc comments and seeing the results you may find yourself confused and dismayed when, for some unknown reason, your visual cues revert to generic blue or maroon jscript members and you lose the summary tooltip.

Say you have an object similar to our MyClass object. New one up, then add an event handler. So far so good. Now assign a value to publicField. Dot your instance… Oops, add_eventHandler has reverted icons and lost its tooltip! This odd behavior appears to be a side effect of the intellisense parsing process and represents a serious bug that I doubt will get any attention this late in the life of VS08. If you are scoped when it breaks, rest assured it will be normal when starting a different scope but if you break it at the window level, well… no more purty pitchers and tooltips for you for the rest of that file.

While the visual cues dissipate, the type inference afforded by the <returns/> tag are still functional. This in itself justifies the effort and the rest is gravy.

And I know what you are thinking…. “Sky, what do you expect when you emplace XML doc comments that you have been advised by those on high should not be emplaced?”. To which I respond: Of course I have given much time and effort in implementing jscript doc comments in the prescribed manner in the interest of resolving this odd behavior and it simply made no difference. Ultimately, I believe the proscription of certain tags by Bertrand is motivated by the fact that they may confuse the current automatic documentation generation implementation. And since that implementation only handles Ajax classes in the context of MicrosoftAjax, this does not concern me much. I have an dormant POJO documentation generator project just waiting to be revived.

There is encouraging news, though. This odd behavior appears to have been remedied in VS10 RC. I was only able to uncover one obvious bug in the Jscript intellisense for VS10 in the handling of Enum field tag content and submitted the bug. It is nice to know that all of this effort is not to be wasted when migrating to VS10.

Note: VS08 Jscript intellisense only reliably works in ‘other’ files., i.e. you will not reliably see intellisense cues for the code in the currently active file. This has been fixed in VS10. For VS08, simply place your libs in ‘other’ files and use script and reference tags. And VS08 JScript Intellisense does not work reliably at all unless you patch your VS. Although the patch specifies x86, it is meant for x64 as well. And a good thing that is because there is no x64 specific version ;-)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)