Investigating Code Snippet Technology

 

Andrew W. Troelsen, Microsoft MVP
Intertech Training

August 2004

Summary: This article explores the use of Visual Studio 2005 and Visual C# 2005 Express Edition code snippet technologies. Once the reader understands the XML syntax used to represent code snippets, this article examines the process of building and registering custom code expansions with your Microsoft .NET IDE of choice. (21 printed pages)

Download the Code.msi sample file.

**Note   **This article assumes you're familiar with the C# programming language and the .NET platform. A basic understanding of XML will also prove helpful.

**Applies to:
**   Microsoft Visual Studio 2005
   Microsoft Visual C# 2005 Express Edition

Contents

Understanding the Role of Code Snippets
A Catalog of Code Snippets
Locating Predefined Code Snippet Files
Code Snippets Under the Hood
Building Custom Code Snippets
Building the New Type Instantiation Expansion
Building the New Web Method Expansion
Building the Disposable Type Expansion
About $selected$
Registering Custom Code Snippets
Summary

Understanding the Role of Code Snippets

Visual Studio 2005 and Visual C# Express Edition both support a technology dubbed code snippets, which are the foundation for two related code-generation techniques:

  • Expansion Templates: The ability of the IDE to generate type definitions, member definitions, and common coding constructs.
  • Surround With IntelliSense: The ability of the IDE to surround a group of selected code statements within a relevant coding construct.

All in all, code snippets technology exists for a single reason—developer productivity. Expansion templates and Surround With IntelliSense enable developers to rapidly generate blocks of code using any of the following approaches:

  • The Edit | IntelliSense menu of the IDE
  • IDE-dependent keyboard shortcuts
  • Context-sensitive mouse right-clicks
  • Typing a snippet's registered shortcut (as defined by a snippet's XML file)

An Expansion Template Example

To illustrate the role of Expansion templates, assume you have created a C# class type (named SportsCar) and wish to quickly add a property with a related backing field. Rather than manually declaring the member variable and authoring the property syntax by hand, you can activate the property Expansion (using any of the previously mentioned techniques) that automates the entire process.

For example, place the mouse cursor within the scope of the type definition and select the Edit | IntelliSense | Insert Expansion menu item. From the resulting list of choices, select property as shown in Figure 1 below.

ms379562.codesnippets-fig1(en-US,VS.80).gif

Figure 1. Activating a Code Expansion

Once this code snippet has been activated, you will find the following property / field declaration within the SportsCar type:

private int myVar;

public int MyProperty
{
  get { return myVar; }
  set { myVar = value; }
}

As you can see, the expansion is intentionally generic. However, if you look a bit closer at the expansion in Figure 2, you will notice that the property and field each have their name and type highlighted in yellow rectangles.

ms379562.codesnippets-fig2(en-US,VS.80).gif

Figure 2. Yellow highlighting is your cue to edit the skeleton expansion.

These yellow highlights represent tokens that you will need to edit to finalize the expansion. Using the Tab key, you can navigate between each highlighted item and edit accordingly.

Edits cascade throughout the expansion as required. Thus, if you Tab to the field's type (int by default) and change it to a string, the property's type declaration is updated automatically when you Tab off the item. Likewise, if you Tab to the field name (myVar by default) and update it to _carColor, the get and set scopes of the associated property are updated automatically as shown in Figure 3.

ms379562.codesnippets-fig3(en-US,VS.80).gif

Figure 3. Sorting out the details of the 'property' Code Expansion

A Surround With IntelliSense Example

Surround With IntelliSense is another example of code snippet technology. Like Expansion templates, Surround With IntelliSense technology also results in code generation. The distinction, however, is that Surround With IntelliSense based code snippets allows you to select a block of code statements before applying the expansion.

To illustrate, assume you now wish to place the SportsCar type into a region using the C# #region / #endregion directives. First, select the code statements to be wrapped within the region and activate the Edit | IntelliSense | Surround With... menu selection (Figure 4).

ms379562.codesnippets-fig4(en-US,VS.80).gif

Figure 4. Selecting statements for Surround With IntelliSense

From the resulting list, select #region and edit the region's name as you see fit using Tab completion.

Note   Surround With IntelliSense can be used without selecting a set of statements. If this is the case, the code snippet is simply generated verbatim.

A Catalog of Code Snippets

Visual Studio 2005 and Visual C# 2005 Express Edition define the same intrinsic code snippets. The following table documents some (but not all) common Surround With code snippets.

Surround With Snippet Definition
#if

#region

These options allow you to wrap code with various C# preprocessor directives.
do

while

foreach

for – iteration by index

Reverse for – iteration by index

These options wrap code within various C# looping constructs.
if statement

else

Wraps a code block within if/else logic.
lock Wraps a code block within a lock scope.
namespace Wraps a collection of types (or namespaces) into a new namespace.
Try Catch

Try Finally

Wraps a block of code within structured exception logic.
Using Wraps a statement inside a C# 'using scope' to ensure object disposal.

The next table documents some (but not all) of the Expansion templates provided by the Visual Studio 2005 and Visual C# 2005 Express Edition IDEs.

Expansion Template Snippet Definition
class

interface

enum

struct

These options generate an empty type definition.
Named Iterator / Indexer pair Iterators are constructs that allow you to build custom collections that expose their sub-objects using an array like index syntax.
Basic attribute implementation Creates a new custom .NET attribute definition (according to best practices).
Destructor Stubs out a C# destructor (which is an overridden System.Object.Finalize in disguise).
Exception type Creates a definition for a custom exception (according to best practices).
Override System.Object.Equals Overrides the virtual Equals() method inherited from System.Object.
Property

Propertyg

Defines a read/write property (property) or read-only property definition (propertyg).

Now, despite what you may be thinking, the remainder of this article will not illustrate the use of each code snippet. I am quite sure you are able to investigate each option at your leisure.

What I will do is examine how a code snippet is composed under the hood. Once this is understood, you will learn how to create and register your own custom code snippets for use by Visual Studio 2005 and Visual C# 2005 Express Edition .

Locating Predefined Code Snippet Files

If you have been working with the .NET platform for any length of time, it should come as no surprise that code snippets are represented using XML (these days, what isn't expressed as XML). Each code snippet is stored in a unique *.xml file, located by default under:

<drive>:\Program Files\Microsoft Visual Studio 8\VC#\ Expansions\1033\Expansions 

Note   This path is based on beta versions of Visual Studio 2005 / Visual C# 2005 Express Edition. If you are unable to locate the Expansions folder as listed above, simply search your machine for a file named class.xml. Assuming the name of this file has not changed, you should be in good shape.

Code Snippets Under the Hood

Now that you have (hopefully) located the *.xml files that represent the default code snippets, let's have a look at the details. Open the class.xml file, which is responsible for inserting a new class definition:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
  <Header>
    <Title>class</Title>
    <Shortcut>class</Shortcut>
    <Description>Expansion snippet for class</Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
      <Literal default="true">
        <ID>name</ID>
        <ToolTip>Class name</ToolTip>
        <Default>MyClass</Default>
      </Literal>
    </Declarations>
   <Code Language="csharp" Format="CData">
    <![CDATA[class $name$
    {
      $selected$$end$
    }]]>
   </Code>
  </Snippet>
</CodeSnippet>

The root element for any code snippet is appropriately named <CodeSnippet>. Within this element scope are two key sub-elements: <Header> and <Snippet>.

The <Header> Element

The <Header> element is used to describe the basic characteristics of the code snippet itself. The following table documents the key sub-elements of <Header>.

<Header> Sub-Element Definition
<Title> The display title for the code snippet.
<Shortcut> Defines the shortcut for the code snippet.

In the IDE, type in the shortcut name to select the snippet followed by the Tab. If you type a partial shortcut name ( 'cla' rather than 'class' for example), you will need to Tab twice; once to complete the named expansion and again to insert the snippet.

<Description> A human-readable description of the snippet, which is displayed when selecting a snippet from the IDE.
<SnippetType> Specifies which category the code snippet belongs to (Expansion, SurroundsWith or Refactoring). Do note that a single code snippet may belong to more than one group.

Microsoft IDEs use this value to determine which context menu will be used to display the code snippet.

Just to make sure you understand how these sub-elements are used within Visual Studio, activate the #region code snippet. Now, try to identify where the <Title>, <Description>, and <SnippetType> values are realized within Figure 5 (open the related pp_region.xml file if you need a hint).

ms379562.codesnippets-fig5(en-US,VS.80).gif

Figure 5. The <Title>, <Description> and <SnippetType> elements in action.

Note   Code snippet technology is the foundation for refactoring support within a Microsoft IDE. To learn more about refactoring technology, consult the following article.

The <Snippet> Element

The <Snippet> sub-element is a bit more complex than <Header>, given that this is where the real action happens. In a nutshell, the <Snippet> element defines the following information:

  • The set of 'variables' to be generated by the expansion. These are the yellow-highlighted-Tab-key-navigated tokens explained earlier in the article.
  • The C# skeleton code to be generated once the expansion is activated.

The <Snippet> element uses the <Declarations> element to account for the expansion variables, each of which is represented by unique <Literal> element. Consider again the class.xml code snippet. This expansion needs only a single <Literal> to represent the class name:

<Declarations>
  <Literal default="true">
    <ID>name</ID>
    <ToolTip>Class name</ToolTip>
    <Default>MyClass</Default>
  </Literal>
</Declarations>

The following table documents the core sub-elements that can appear within a <Literal> node.

<Literal> Sub-Element Definition
<ID> This value represents the name used to identify the variable in the code expansion.
<ToolTip> This value is used by the IDE to display a tool tip when the mouse cursor is hovering over a yellow-highlighted variable.
<Default> Specifies the default value for the code snippet variable.

Again, just to be sure you can map these elements to the IDE, ponder Figure 6, which shows the result of the class snippet expansion.

ms379562.codesnippets-fig6(en-US,VS.80).gif

Figure 6. The <ToolTip> and <Default> elements in action

The <Code> Element

Finally, we need to address the role of the <Code> sub-element defined within <Snippet>. This element is used to define the code to be inserted, represented by a CDATA section (recall that CDATA sections are used to defined data that is not well-formed XML, but still required by the XML document). Here is the <Code> element for the class.xml code snippet:

<Code Language="csharp" Format="CData">
  <![CDATA[class $name$
  {
    $selected$$end$
  }]]>
</Code>

Notice the various names sandwiched by $ tokens (such as $name$). This syntax is used to reference the variables defined within a <Literal> element, as well as predefined literals understood by Visual Studio 2005 and Visual C# 2005 Express Edition (such as $selected$).

Also note that the $name$ token is referring to the name <Literal> defined in the <Declarations> section of the class.xml document. The $selected$ token represents the code statements selected within the IDE before the expansion was activated. As you would guess, $end$ is used to mark where the user's cursor will be placed after the snippet has expanded.

Building Custom Code Snippets

If you are an XML superstar, the previous section should be about all you need to define your custom code snippets (of course, I have not yet described how to register custom code snippets with Visual Studio, so keep reading). However, for those of you wish to see some concrete examples, allow me to present a few custom snippets examples, which I hope will increase your workday productivity to some extent:

  • The New Type Instantiation Expansion
  • The New Web Method Expansion
  • The Disposable Type Expansion

If you wish to quickly leverage these code snippets, simply copy and paste the following XML documents into an appropriately named *.xml file. The final section of this article will explain how to register custom code snippets with the Visual Studio 2005 and Visual C# 2005 Express Edition IDEs.

Building the New Type Instantiation Expansion

The first code snippet we will create, newobj, is responsible for generating the following C# statement (shown in pseudo-code):

[typeName] [varName] = new [typeName]();

The [typeName] placeholder will be controlled by a <Literal> aptly named typename and will have a default value of object. The [varName] placeholder will be controlled by another <Literal> named varname, which has the default value of 'obj'. Therefore, once this code snippet is applied, you will find the expansion shown in Figure 7.

ms379562.codesnippets-fig7(en-US,VS.80).gif

Figure 7. The newobj code snippet in action

Create a new XML document (newobj.xml) and enter the following:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
  <Header>
    <Title>new object</Title>
    <Shortcut>newobj</Shortcut>
    <Description>Expansion snippet to allocate a new type
    </Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>typename</ID>
        <ToolTip>type name</ToolTip>
        <Default>object</Default>
      </Literal>
      <Literal>
        <ID>varname</ID>
        <ToolTip>variable name</ToolTip>
        <Default>obj</Default>
      </Literal> 
    </Declarations>
      <Code Language="csharp" Format="CData">
        <![CDATA[$typename$ $varname$ = new $typename$ ($end$);]]>
      </Code>
  </Snippet>
</CodeSnippet>

The current version of newobj.xml generates code that triggers the type's default (no argument) constructor. One enhancement you could make is to add a third <Literal> that represents a C-style code comment:

<Literal>
  <ID>ctorArgs</ID>
  <ToolTip>Type in your ctor args</ToolTip>
  <Default>/* Type Args Here */</Default>
</Literal>

We could then update our CDATA section as so:

<Code Language="csharp" Format="CData">
  <![CDATA[$typename$ $varname$ = new $typename$ ($ctorArgs$ $end$);]]>
</Code>

The end result is that we have provided a Tab-reachable placeholder where constructor arguments can be manually entered like the one shown in Figure 8.

ms379562.codesnippets-fig8(en-US,VS.80).gif

Figure 8. The improved newobj code snippet

Building the New Web Method Expansion

When you are building XML Web Services using the .NET platform, the almighty [WebMethod] attribute does a vast majority of the hard work. As you know, each public method adorned with [WebMethod] can be reached using HTTP requests and return its value like XML data representation. In the simplest format, [WebMethod] takes no arguments:

[WebMethod]
public SportsCar GetNewCar()
{
  return new SportsCar();
}

However, the [WebMethod] attribute can take a number of optional named parameters, one of which is Description:

[WebMethod(Description = "This method returns a new SportsCar"]
public SportsCar GetNewCar()
{
  return new SportsCar();
}

Given this, our next code snippet, newWM, will account for the Description named parameter, as well as <Literal> elements that represent the method's return value and name. Figure 9 shows newWM in action:

ms379562.codesnippets-fig9(en-US,VS.80).gif

Figure 9. The newWM code snippet in action

If you followed the logic behind the previous code snippet, the contents of newwebmethod.xml should be straightforward:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
  <Header>
    <Title>new web method</Title>
    <Shortcut>newWM</Shortcut>
    <Description>Expansion snippet to generate a new Web Method
    </Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>retval</ID>
        <ToolTip>return value</ToolTip>
        <Default>void</Default>
      </Literal>
      <Literal>
        <ID>methodname</ID>
        <ToolTip>method name</ToolTip>
        <Default>MyWebMethod</Default>
      </Literal> 
      <Literal>
        <ID>desc</ID>
        <ToolTip>Description of Web Method</ToolTip>
        <Default>This is my Web Method</Default>
      </Literal>         
    </Declarations>
    <Code Language="csharp" Format="CData">
      <![CDATA[
      [WebMethod(Description="$desc$")]
      public $retval$ $methodname$()
      {
        $end$
      }
      ]]>
    </Code>
  </Snippet>
</CodeSnippet>

Note   To compile the source code generated by the newWM expansion, your project will need to reference the System.Web.Services.dll assembly. The C# source code file will also need to specify it is using the System.Web.Services namespace.

Building the Disposable Type Expansion

Code snippets can expand to as much code as you feel is necessary. For example, the Exception Type expansion generates an entire class deriving from System.ApplicationException that conforms to .NET best practices. The Basic attribute implementation code snippet expands to a new System.Attribute derived type (also constructed according to .NET best practices).

In a similar vein, we will wrap up our examination of building an Expansion template that is responsible for building a disposable type. As you may know, when you are creating a type that interacts with unmanaged resources, best practice dictates that you:

  1. Implement the IDisposable interface. The object user should (ideally) call the defined Dispose() method to release any unmanaged resources.
  2. Override System.Object.Finalize: If the object user forgets to call the Dispose() method, the CLR garbage collector will (eventually) call the type's destructor.

The dispclass code snippet will build such a type, shown activated in Figure 10.

ms379562.codesnippets-fig10(en-US,VS.80).gif

Figure 10. The dispclass code snippet in action

Here is the XML, which I would guess requires no comment at this point.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
  <Header>
    <Title>disposable class</Title>
    <Shortcut>dispclass</Shortcut>
    <Description>Expansion snippet for a disposable class
    </Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>classname</ID>
        <ToolTip>Class name</ToolTip>
        <Default>MyResourceWrapper</Default>
      </Literal>
    </Declarations>
    <Code Language="csharp" Format="CData">
    <![CDATA[class $classname$ : IDisposable
    {
      ~$classname$()
      {
         CleanUp(false);
      }
      public void Dispose()
      {
         CleanUp(true);
         GC.SuppressFinalize(this);
      }
      protected void CleanUp(bool disposing)
      {
         if (disposing) 
         {
            // Free other state (managed objects).
         }
         // Free your own state (unmanaged objects).
         // Set large fields to null.
      }
    }]]>
    </Code>
  </Snippet>
</CodeSnippet>

About $selected$

Each of the expansions we have created belong to the Expansion snippet category:

<SnippetTypes>
  <SnippetType>Expansion</SnippetType>
</SnippetTypes>

If the <SnippetType> element is set to SurroundsWith (rather than Expansion), the code snippet appears within the Surround With context menu. Recall that Surround With code snippets typically make use of a predefined literal named $selected$, which represents the statements selected in the code editor.

For the sake of illustration, here is a simple example of using the $selected$ token to wrap selected code statements in a defining scope (note that this snippet belongs to the Expansion and SurroundsWith categories):

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
  <Header>
    <Title>dummy scope</Title>
    <Shortcut>dscope</Shortcut>
    <Description>Surrounds selected code within a dummy scope
    </Description>
    <SnippetTypes>
      <SnippetType>Expansion</SnippetType>
      <SnippetType>SurroundsWith</SnippetType>
    </SnippetTypes>
  </Header>
  <Snippet>
    <Code Language="csharp" Format="CData">
    <![CDATA[ 
    {
      $selected$ $end$
    }]]>
    </Code>
  </Snippet>
</CodeSnippet>

Again, this example is purely illustrative given that we don't gain much by grouping code within a dummy scope. Nevertheless, when you are building custom code snippets, remember that the $selected$ token allows you to capture the current selection within the IDE.

Registering Custom Code Snippets

Now that we have a set of custom code snippets, we need to advertise their existence to Visual Studio. Luckily for us, Visual Studio 2005 and Visual C# 2005 Express Edition share common approaches to do so.

First of all, you could opt to simply copy your *.xml files to the directory containing the out of the box code expansion files as shown in Figure 11.

ms379562.codesnippets-fig11(en-US,VS.80).gif

Figure 11. Registering custom code snippets in the default Expansions folder

If you follow this approach, you will see your custom code snippets appear alphabetically within the related context menu, shown in Figure 12.

ms379562.codesnippets-fig12(en-US,VS.80).gif

Figure 12. Accessing our code snippets through IntelliSense

The other approach is to create a specific folder for your code snippets (for example, C:\MySnips) and register this location using the Tools | Code Snippet Manager menu selection. Once activated, you are presented with a dialog box like Figure 13 that allows you to specify alternative folders (using the Add button) that contain your XML documents.

ms379562.codesnippets-fig13(en-US,VS.80).gif

Figure 13. Registering a custom snippet location

Note   As suggested by Figure 13, the <Header> element may contain an <Author> sub-element to document the author of the snippet.

With this approach, you will now need to select the folder from the IDE's context menu as shown in Figure 14.

ms379562.codesnippets-fig14(en-US,VS.80).gif

Figure 14. Selecting the custom location

Once you do, you will see your snippets appear as expected.

ms379562.codesnippets-fig15(en-US,VS.80).gif

Figure 15. Selecting the custom snippet (in the custom location)

Now that you have learned the basic lay of the land, you should be in a good position to build any code expansion you may require. As a logical next step, read over the snippets that ship with your Microsoft IDE to further your understanding. Happy coding!

Summary

Visual Studio 2005 and Visual C# 2005 Express Edition each ship with a set of predefined code snippets. Snippets are authored using a small set of XML elements and are used to quickly generate code for common tasks. In addition to leveraging the supplied code snippets, developers are free to build custom Surround With and Expansion templates that can be processed by the IDE once registered.

Andrew Troelsen is a consultant and trainer with Intertech Training. Andrew is the author of numerous books including the award winning C# and the .NET Platform Second Edition (Apress 2002). He also authored a monthly column for (of all places) MacTech where he explored .NET development on Unix-based systems using the SSCLI, Portable.NET and Mono CLI distributions.

© Microsoft Corporation. All rights reserved.