Dynamic code generation has existed for quite a while. In fact, you’ll find tools that can help developers generate tens of thousands of lines of code with just a click of a button. Some of these are even flexible enough to allow you to modify the tokens in the template—or even the templates themselves—for added control over the output of the code. (The Code Generation Network Web site has an extensive list of these tools, quite a few of which I have used in the past—some with great success.)
I have always wanted to have the ability to create code inline without loading an external tool, or loading a Visual Studio add-in. That is where code snippets come to the rescue. They are not as powerful as some of the generators, but they perform more work in the day-to-day coding that experienced developers do.
Visual Studio 2005 IntelliSense code snippets enable you to create your own IntelliSense snippets without leaving the Visual Studio IDE. This ability boosts productivity by allowing you to create and insert samples of code that you use regularly. It also allows custom code developers to provide snippet examples of their own libraries and APIs.
Table 1 shows the current list of included snippets as of this writing.
Table 1: List of Current Visual Studio 2005 IntelliSense Code Snippets
|Name||Description||Valid Locations to Insert Snippet|
|#if||Creates a #if directive and a #endif directive||Anywhere|
|#region||Creates a #region directive and a #endregion directive||Anywhere|
|~||Creates a destructor for the containing class||Inside a class|
|attribute||Creates a declaration for a class that derives from Attribute||Inside a namespace (including the global namespace), a class, or a struct|
|checked||Creates a checked block||Inside a method, an indexer, a property accessor, or an event accessor|
|class||Creates a class declaration||Inside a namespace (including the global namespace), a class, or a struct|
|ctor||Creates a constructor for the containing class||Inside a class|
|cw||Creates a call to WriteLine||Inside a method, an indexer, a property accessor, or an event accessor|
|do||Creates a do while loop||Inside a method, an indexer, a property accessor, or an event accessor|
|else||Creates an else block||Inside a method, an indexer, a property accessor, or an event accessor|
|enum||Creates an enum declaration||Inside a namespace (including the global namespace), a class, or a struct|
|equals||Creates a method declaration that overrides the Equals method defined in the Object class||Inside a class or a struct|
|exception||Creates a declaration for a class that derives from an exception (Exception by default)||Inside a namespace (including the global namespace), a class, or a struct|
|for||Creates a for loop||Inside a method, an indexer, a property accessor, or an event accessor|
|foreach||Creates a foreach loop||Inside a method, an indexer, a property accessor, or an event accessor|
|forr||Creates a for loop that decrements the loop variable after each iteration||Inside a method, an indexer, a property accessor, or an event accessor|
|if||Creates an if block||Inside a method, an indexer, a property accessor, or an event accessor|
|indexer||Creates an indexer declaration||Inside a class or a struct|
|interface||Creates an interface declaration||Inside a namespace (including the global namespace), a class, or a struct|
|invoke||Creates a block that safely invokes an event||Inside a method, an indexer, a property accessor, or an event accessor|
|iterator||Creates an iterator||Inside a class or a struct|
|iterindex||Creates a “named” iterator and indexer pair by using a nested class||Inside a class or a struct|
|lock||Creates a lock block||Inside a method, an indexer, a property accessor, or an event accessor|
|mbox||Creates a call to System.Windows.Forms.MessageBox.Show (You may need to add a reference to System.Windows.Forms.dll.)||Inside a method, an indexer, a property accessor, or an event accessor|
|namespace||Creates a namespace declaration||Inside a namespace (including the global namespace)|
|prop||Creates a property declaration and a backing field||Inside a class or a struct|
|propg||Creates a property declaration with only a “get” accessor and a backing field||Inside a class or a struct|
|sim||Creates a static int Main method declaration||Inside a class or a struct|
|struct||Creates a struct declaration||Inside a namespace (including the global namespace), a class, or a struct|
|svm||Creates a static void Main method declaration||Inside a class or a struct|
|switch||Creates a switch block||Inside a method, an indexer, a property accessor, or an event accessor|
|try||Creates a try-catch block||Inside a method, an indexer, a property accessor, or an event accessor|
|tryf||Creates a try-finally block||Inside a method, an indexer, a property accessor, or an event accessor|
|unchecked||Creates an unchecked block||Inside a method, an indexer, a property accessor, or an event accessor|
|unsafe||Creates an unsafe block||Inside a method, an indexer, a property accessor, or an event accessor|
|using||Creates a using directive||Inside a namespace (including the global namespace)|
|while||Creates a while loop||Inside a method, an indexer, a property accessor, or an event accessor|
Take a Shallow Dive
To access code snippets, simply right-click in a code window and click on ‘Insert Snippet’, as shown in Figure 1.
Figure 1: Access Code Snippets
Depending on the type of file, you then will see the types of snippets that are available to you. This example presents a C# class file, so you see two options: Office Development and Visual C#, as shown in Figure 2.
Figure 2: Available Types of Snippets
The category you select displays a list of its available snippets. These snippets will vary; you add to them or modify the existing ones. The list in Figure 3 is from the Visual C# category.
Figure 3: Available Snippets in Visual C#
The snippet name that you select dictates the code that is injected into the code window and at which location. For this C# example, choose ctor, which is the snippet to create a default constructor for your class (see Figure 4).
Figure 4: The ctor Code Snippet Creates a Default Constructor for Your Class
Clicking on the label injects the code into the spot where your cursor was when you first right-clicked in the code window. Figure 5 shows the code.
Figure 5: The Injected ctor Code
Now you can explore the ctor.snippet file that is located in the folder C:Program FilesMicrosoft Visual Studio 8VC#Snippets1033Visual C#. Figure 6 shows this snippet file.
Figure 6: The ctor.snippet File
The file has only one code snippet defined, but it can have as many as defined in the schema. The title element will be displayed in the IntelliSense picker as well as in the Code Snippets Manager (see Figure 7). The manager also shows the other header elements; the Code Snippets Manager window displays the description, shortcut, snippet type, and the author.
Figure 7: Title and Header Elements in the Code Snippets Manager
The Elements of a Snippet
You now are ready to learn what it takes to create a snippet. You’ll find the snippet schema at snippetformat.xsd. CodeSnippets is the root element of the schema. It can contain one or more CodeSnippet child elements, each of which contains all the data for the snippet:
The CodeSnippet element contains two child elements, as well as an optional format attribute. The format attribute is a mechanism that enables you to version the snippet:
The Header element contains general information about the snippet, and each code snippet can contain only one. The child elements of the header are Title, Author, Description, HelpUrl, SnippetTypes, Keyword, and Shortcut:
The following is the Header from the ctor.snippet file in this example:
<Description>Code snippet for constructor</Description>
Most of these elements are pretty self-explanatory. The HelpUrl is a pointer to a document that contains additional information about the snippet. SnippetTypes indicates to Visual Studio 2005 how to insert the code snippet into the code window:
If this value does not exist, code can be inserted into any file. The following are the valid SnippetType values:
- SurroundsWith: Place the code snippet around a selected piece of code
- Expansion: Insert the code snippet at the cursor
- Refactoring: Use the code snippet during Visual C# refactoring (Refactoring cannot be used in custom code snippets.)
Because refactoring is not allowed in custom code snippets, that leaves SurroundsWith and Expansion. The main difference between the two is shown in the figures below. To use the SurroundsWith type, you must select the code or text block that you want to ‘surround’ with the snippet. Figures 8 and 9 show the before and after of the SurroundsWith type.
Figure 8: Before the SurroundsWith Type
After selecting the code, you then can right-click and select the snippet. This example selects the region snippet.
Figure 9: After the SurroundsWith Type
Selecting the #region snippet encases the code in #region and #endregion tags. Also note that the default substitution string MyRegion is highlighted. If you needed to make more replacements, you could simply tab to the next one. This feature speeds up the maintenance of the snippet. The expansion snippet, on the other hand, just inserts the code at the location of the cursor.
The Keywords elements allows one or more keyword elements to be defined so that Visual Studio and other providers can search and locate the snippet in a standard way:
And finally comes the Shortcut key, a unique feature that allows you to just type the shortcut into the code window and then press the tab key. This works great with expansion snippets.
You can now look at the Snippet element, which defines the code for the snippet:
The following is the snippet node from the example ctor.snippet:
<Code Language=”csharp”><![CDATA[public $classname$ ()
The References element is valid only in Visual Basic snippets. This optional element contains information about references to other libraries that may be used in the snippet. It has two child elements: Assembly is the location of the dll, and Url is a link to more information about the assembly:
The Imports element contains information about the namespaces that will be used in the code snippet. It has the same effect as using the import inside your code. The only child element of the Imports element is the namespace:
The Declarations element is where you define variables that act as replacements within the code element of the snippet. Literal and Object are the two kinds of replacements. Literal replacements take the form of know types, such as string, numbers, or dates. Object replacements are defined outside the snippet. The following is an example of both:
<ToolTip>Replace with a connection object in your
The attribute on the Literal element, Editable, determines whether or not you can edit the value of the literal once it is inserted. The default is True. The following are the other element descriptions:
- Default (Required element): Specifies the literal’s default value when you insert the code snippet. A Literal element must contain exactly one Default element.
- Function (Optional element): Specifies a function to execute when the literal receives focus in Visual Studio. A Literal element may contain zero or one Function element.
- ID (Required element): Specifies a unique identifier for the literal. A Literal element must contain exactly one ID element in a Literal element.
- Tooltip (Optional element): Describes the expected value and usage of the literal. A Literal element may contain zero or one Tooltip elements.
The Function element has four functions available for pre-processing, and they can aid in returning some pretty interesting results. They are GenerateSwitchCases(EnumerationLiteral), ClassName(), SimpleTypeName(TypeName), and CallBase(Parameter). They each provide a unique result, which is worth exploring in greater detail. (My next article will dive deep into snippet functions.)
You now can look at the code element, which is where you will put your actual code, with the replacement variables to output your desired text. The code element has three attributes and no child elements. The attributes are kind, delimiter, and language. The delimiter, by default, is $. This character delimits literal and object variables in the snippet code text. The Language attribute, which is required, can be any of the following:
- VB: Identifies a Visual Basic code snippet
- CSharp: Identifies a Visual C# code snippet
- VJSharp: Identifies a Visual J# code snippet
- XML: Identifies an XML code snippet
This identifies the code snippet’s language. The actual code element is text data that is surrounded by CDATA, so that it is taken as a literal string inside the element. The following is the code element from the ctor.snippet:
<Code Language=”csharp”><![CDATA[public $classname$ ()
So, if you look at the output of the snippet, you can pretty easily follow along with what the IDE is generating and why:
- The attribute language specifies that it is C#.
- The code element begins with the CDATA escape, and then the word public.
- The replacement variable $classname$ is the result of the function ClassName, and then comes the two parentheses after the class name. As you can see, the placement is in the same location, based on the location of the cursor. This will also be affected by the options that are set in Visual Studio 2005 for C# formatting.
- The next line has the open bracket, followed by $end$ on the next line, and then the closing bracket. The $end$ is one of a set of predefined Literals variables. It is used to indicate the end of a line or statement. When the user presses ENTER to finish editing the code snippet fields, this variable determines where to move the caret (^).
Time to Make Your Own
Now, you can create a real simple snippet that you can use in your own code. Because all of your code should have documentation, create a file header snippet that you can tailor to your own needs. Then, you can extend it with another snippet for when you make changes to the file.
Figure 10 shows the output you want to get with your code snippet.
Figure 10: Desired Output from Your Code Snippet
I have defined four Literal variables that will replace strings in the above template. Classname will be the prefix to the file name. The Author will be replaced by the $author$ Literal variable. The $date$ variable will replace the create date and the history date, and the $company$ Literal will replace Developer.com. Figure 11 shows the final XML snippet file.
Figure 11: The Final XML Snippet File
You can use this new snippet with just a few clicks of the mouse. Start by loading the Snippet Manager (see Figure 12).
Figure 12: Load the Snippet Manager
Highlight “My Code Snippets” and click the Import button. Navigate to the location where you saved the header.snippet file and select open (see Figure 13).
Figure 13: Navigate to the header.snippet File and Select Open
On the left side of the dialog window, click the checkbox next to “My Code Snippets” and click the Finish button (see Figure 14).
Figure 14: Click the Checkbox Next to “My Code Snippets” and Click Finish
You now can open the “My Code Snippets” node and highlight the c3header snippet (see Figure 15). This will show some of the general attributes that you assigned to the snippet in the XML file.
Figure 15: Open the “My Code Snippets” Node and Highlight the c3header Snippet
Now, you can go to a C# class file and test out your new snippet. Figure 16 shows the output.
Figure 16: Output from Testing Out Your New Snippet
It may not seem like a lot, but the fact that you can type the shortcut and insert your own header is only the beginning of what you will be able to do.
Even though that was not a lot of steps, the fact that you dealt with raw XML might be a little more than you want to handle. So, the next two sections show you how I mass-produce my snippets, as well as an open source project that allows you to create and test your snippets.
I found myself creating code snippets for a lot of the same methods that I was overriding. So, I created a Visual Studio Macro to create a snippet out of the code that I selected. Download the macro and the test project. Load the macro in Visual Studio, and then highlight the code out of which you want to create a snippet (see Figure 17).
Figure 17: Highlight the Code Out of Which You Want to Create a Snippet
From the macro explorer, double-click the CreateSnippet macro (see Figure 18).
Figure 18: Double Click the CreateSnippet Macro
The screen will wiggle, and then you will have the output in a new file in the solution explorer. The file content from what you selected above will look like Figure 19.
Figure 19: The File Content from Your Selections
It’s still a little raw, but you should be able to create your own templates to help generate snippets pretty quickly.
Visual Basic Code Snippet Editor
The open source project I mentioned at the end of the previous section is the Visual Basic Code Snippet Editor, a tool that enables you to create, edit, and test your code snippets. Take the time to download the Visual Studio 2005 edition from the GotDotNet Workspace. The enhancements from the Beta 2 version are well worth it. It is still not completed, but the team working on this is doing a great job. Figure 20 shows a screen shot.
Figure 20: Screenshot of Visual Basic Code Snippet Editor
Your Own Snippet Library
Take the Visual Basic Code Snippet Editor for a spin and try some of the tips in this article and the Visual Studio 2005 BOL. You will have a nice library of your own snippets in no time. I hope this tutorial gets you to explore the new features of IntelliSense code snippet technology in Visual Studio 2005.
Download the Code
Click here to download the snippet macro and the test project.
About the Author
Patrick Gallucci is a Sr. Consultant in the Communications Sector at Microsoft. He has more than thirteen years experience in information technology. Patrick’s area of focus is developing operational efficiencies using development tools and technologies such as .NET, SQL Server, BizTalk Server, Content Management Server, and Commerce Server. His certifications include MCSE, MCDBA and MCT. Patrick is a recent instructor of MSF Design, Microsoft C#, ASP.NET, and other .NET classes. You can contact Patrick at [email protected].