Dissecting Snippets (Matt Gertz)

Dissecting Snippets (Matt Gertz)

  • Comments 2

The snippet feature is one of the features that I’m fondest of in Visual Basic.  It was another one of a handful of features that got discussed in a series of “What if…” meetings that Sam & I had during the “Whidbey” planning stages.  As I recall, the things that motivated it for Sam was demos, and for me it was macros.  Visual Studio already had the capability to allow users to drag text to the toolbox to be reused elsewhere in the code, a time-saver frequently used by program managers during demos, but the code would often need to be modified for different situations, often in ways that weren’t always so obvious.  Macros, which I was using quite a lot in my coding, could be a bit more flexible but harder to get to (unless you spent time setting up menus or buttons or whatnot), and their flexibility was totally dependent on how much time you wanted to put into the macro.  Distribution in either case was quite problematic.  Neither option was satisfactory to either of us, and we although we had some idea of how a UI to address this might look, we weren't making much progress on it.  At the same time, however, Amanda had moved ahead with a concept called "ExStencil" which involved shared code templates, and it became very clear that Amanda's direction was the right way to solve this problem.  We merged the different scenarios into her feature in which lines of text that could be saved with “variables” which could be filled in by the user when the snippet was inserted, and thus snippets were born. 

[Edit: I rewrote that last paragraph somewhat after the first posting -- my brain had clearly been malfunctioning by forgetting the ExStencil work first time around.]

Using snippets is pretty easy.  You can access them in a couple of ways, by either right-clicking in code and choosing “Insert Snippets” to bring up the snippet menu, or just type “?” + “TAB” as a shortcut.  It’s then just a matter of navigating to the snippet you’re interested in.  Also, each snippet has a shortcut associated with it, so if you know that shortcut, you can just type it and press “TAB” to have it inserted.  (You can find out a snippet’s shortcut for “future information” by selecting it in the snippet menu – it’ll show up in the resulting tooltip.) Once the snippet is in, you can tab around the highlighted fields and fill in the information that’s specific to your application.

At the time we were working on developing snippets, we’d planned on having a built-in snippet editor as well (it even shipped as part of the first beta of VS2005), but the UI for it was pretty buggy and awkward, and so time constraints forced us to pull it from the product rather than fix it up.  (That was probably my most painful moment during that product cycle.)  Fortunately, the community stepped up, and a shared source snippet editor is available at http://msdn2.microsoft.com/en-us/vbasic/ms789085.aspx -- check it out!

Even without the snippet editor, snippets aren’t too hard to understand or to build.  They use a fairly simple schema that can be found at http://msdn2.microsoft.com/en-us/library/ms171418(VS.80).aspx, and are stored in *.snippet files.  Let’s dissect one of the VS2005 snippets here.  The VB snippets live in your installation at c:\Program Files\Microsoft Visual Studio 8\VB\Snippets (your drive letter may vary).  Let’s take a look at the “appLog” snippet (formally called “Write to a Text File using ‘My’”), which adds the following code:

        My.Computer.FileSystem.WriteAllText( "TheFile.txt", "TextContents", False)

 

Opening it up in (for example) Notepad, you’ll note that it’s all written in XML, and starts out with typical XML goo which points the application back to our schema:

<?xml version="1.0"?>

<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">

  <CodeSnippet Format="1.0.0">

 

The most interesting part in that section is the Format attribute.  The intention is that future versions of Visual Studio should be able to respond correctly for older snippets if the format for snippets ever changes.  

Having opened the CodeSnippet tag, we then move on to the basic information:

    <Header>

      <Title>Write to a Text file</Title>

      <Author>Microsoft Corporation</Author>

      <Description>Writes a message to the application event log.</Description>

      <Shortcut>appLog</Shortcut>

    </Header>

 

This is all fairly self-explanatory.  The title will show up in the snippet menu, the description will show up in the tooltip, and “appLog” is the shortcut used when you want to insert the snippet without going through a menu (it will also show up in the tooltip).

We now progress to the actual snippet block:

    <Snippet>

 

which includes a number of parts such as these:

      <References>

        <Reference>

          <Assembly>System.dll</Assembly>

        </Reference>

      </References>

 

      <Imports>

        <Import>

          <Namespace>System</Namespace>

        </Import>

        <Import>

          <Namespace>Microsoft.VisualBasic</Namespace>

        </Import>

      </Imports>

 

The goal here is for the application to add the specified references and imports if they don’t already exist in the code.  (The ones shown above are going to be implied in the project already, but that won’t be the general case.)

The snippet block also contains a “Declarations” block, which is how the replaceable fields are defined.  In this snippet, there are three replaceable fields, contained as “Literals”:

      <Declarations>

 

        <Literal>

          <ID>fileName</ID>

          <Type>String</Type>

          <ToolTip>The String variable that stores the filename to be written to.</ToolTip>

          <Default>"TheFile.txt"</Default>

        </Literal>

 

        <Literal>

          <ID>textContents</ID>

          <Type>String</Type>

          <ToolTip>The text to be written to the file.</ToolTip>

          <Default>"TextContents"</Default>

        </Literal>

 

        <Literal>

          <ID>append</ID>

          <Type>Boolean</Type>

          <ToolTip>Specifies whether text should be added to the end of the file (true) or overwrite existing contents (false).</ToolTip>

          <Default>False</Default>

        </Literal>

 

      </Declarations>

 

Note that each literal has some sort of identifier (the “ID” -- think of it as a variable name for that field), the Type of the field (e.g., string), a ToolTip which the user will see when hovering over the field, and some Default value used as a placeholder until the user changes it.  (The Literal tag doesn’t actually require a “Type” tag, and the VB editor doesn’t use it – in this case, it’s essentially a comment.)

Finally, all that’s left is to write the actual code.  We use a CDATA block to specify it:

      <Code Language="VB" Kind="method body">

<![CDATA[My.Computer.FileSystem.WriteAllText($fileName$,

 $textContents$, $append$)]]>

      </Code>

    </Snippet>

  </CodeSnippet>

</CodeSnippets>

And that’s it!  The attribute "Language=VB" is self-explanatory, and the "Kind" attribute indicates that the proper scope of the snippet is inside a method body.  Note that the ID’s specified by the Literal tags in the previous section are used here, with the “$” character on either end of the ID reference to indicate that it’s a replaceable field.  You can use literals more than once in the CDATA block.  If you do this, then when the user inserts your snippet and changes a field which references that literal, the other field(s) with that same reference will automatically update as well – very cool!

So, once you’ve created your snippet, you’ll want to make it available to yourself in Visual Studio.  To do this, choose “Tools,” then “Code Snippets Manager.”  This will bring up a dialog which will allow you to control which snippets show up in Visual Studio.  Beneath a combobox which decides which language of snippets to show (VB or C#, for example), there’s a listview that shows you what’s currently in the path (organized by folder), and a description pane shows you the details for the snippet selected in that listview.  The “Add” button allows you to point to a particular directory which will then be included in the snippet search path, while “Remove” allows you remove the selected folder from consideration.  “Import” allows you to bring in a snippet directly, regardless of the folder it lives in, and assign it to an existing code snippet folder.  Finally, “Search Online” will help you locate other snippets that are available on the Net.

There’s one consideration you should ponder before making a snippet.  Once the file is closed, any snippet information associated with the insertion is gone, and even were that not the case, a subsequent change to your snippet in its *.snippet file won’t affect any previous usages of the snippet.  Therefore, snippets shouldn’t be used as replacements for methods (Subs or Functions).  Ideally, code should be modular and reusable, and snippets are really intended for smaller, commonly-required tasks.  Sure, you can create the World’s Largest Snippet and use it everywhere, but if you later debug your code and discover that your snippet had a mistake, you’ll have to change code everywhere you previously used that snippet.  For complicated things, a method that takes variables is better, since you can fix any bug in one place.

Next time, I think I’ll write a bit about Edit & Continue, which was certainly the most complicated feature I think that I’ve ever been involved with in nearly 13 years.  ‘Til then…

--Matt--*

Leave a Comment
  • Please add 5 and 4 and type the answer here:
  • Post
  • Hello Matt,

    I agree with you, that the use of snippets is very easy. But the devolpment of an own snippet is very high sophisticated, not transparent and unfortunetly  the literature to this theme is poor. In my opinion, the need to use XML to describe a snippet is no progress. I remember back to my first programming language , Algol68. The number 68 stays for the year 1968. I am sorry, that i can not part your intuition.

  • Hi, Hartmut,

    Indeed, I certainly could not claim that writing a snippet in XML would be easier than writing it in an editor!  I would urge folks who are interested in writing snippets wholesale to check out the community editor that I linked to in the article.  

    However, I did some repair work on the snippets (fixing bugs in them, adding imports and references, etc.) before they shipped in VS2005 and before the community editor was available, and I did all of that in Notepad.  It really wasn't all that bad.  There are things I'd certainly *rather* do, but the XML wasn't so bad to work with.  

    We actually shipped with a different schema for the first beta, one that didn't use CODE blocks, and in many ways I liked it better because it stratified the code into sections that made it easier for the insertion algorithm to better understand what the code was doing, for future growth.  (All of the code was broken down into tags.)  However, I'll admit it is nice using the current schema to just be able to paste a block of code from the editor into the snippet and then tweak it to handle the literals.

    --Matt--*

Page 1 of 1 (2 items)