Visual Studio Code Snippet Generator
A Code Snippet in Visual Studio allows a developer to type a shortcut and then a block of code is inserted where the cursor is. The block of code contains literal values that can be replaced in the templated snippet.
A commonly used code snippet is prop.
- In the class the developer types prop and hits the tab key
- A property declaration replaces the text and the developer has the option to replace the variable type and variable name
- The code snippet highlights in turn the variable type (int) and then the variable name (MyProperty) by hitting the tab key and typing
public int MyProperty { get; set; }
What Makes a Code Snippet
A code snippet is written using XML and is made up from the following elements
- Header Element
- Title - The friendly name of the code Snippet
- Description - a description of the code Snippet
- Author - who wrote the snippet
- Shortcut - the text to type to activate the code snippet
- HelpUrl - optional help url for the snippet
- Keywords - allows code snippets to be grouped in Visual Studio
- SnippetTypes - allows code snippets to be grouped in Visual Studio
- Snippet
- Declarations
- Literal
- ID - text that is to be replaced in the code section
- Tooltip - tooltip to show when typing
- Default - is the default value to put in the code block (for replacing)
- Literal
- Code
- Language - what language the snippet is for C#, VB, JavaScript etc
- Delimiter - the delimiter for the literals that will be replaced in code e.g $
- CData Section - this is where you write your code template and add the literals that need replacing
- Declarations
Create a Serialized Property Snippet
A common task when creating serializable classes in C# is to add attributes for XML and JSON serialization that may differ from the property name. for instance
[XMLElement("Description")]
[JsonProperty("description")]
public string CustomDescription {get;set;}
We are going to create a snippet that takes the following literals (values) which will be replaced when the code snippet is accessed
- Xml Name
- Json Name
- Variable Type
- Variable Name
In addition the shortcut we are going to use is propjx. Below is the output that we want to create.
<?xml version="1.0" encoding="utf-16"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>Serialiazable Property</Title>
<Author>Jonathan Purdom</Author>
<Description>Public Property with XML and JSON Serializable Attributes</Description>
<CodeSnippetTypes />
<KeyWords />
<ShortCut>propjx</ShortCut>
</Header>
<Snippet>
<Code Language="CSharp" Delimiter="$"><![CDATA[
[XmlElement("$XmlName$")]
[JsonProperty("$JsonName$")]
public $VarType$ $VarName$ {get; set;}
]]></Code>
<Declarations>
<Literal>
<ID>XmlName</ID>
<Tooltip>XML Element Name of the Property</Tooltip>
<Default>name</Default>
</Literal>
<Literal>
<ID>JsonName</ID>
<Tooltip>Json Element Name of the Property</Tooltip>
<Default>name</Default>
</Literal>
<Literal>
<ID>VarType</ID>
<Tooltip>Property type</Tooltip>
<Default>string</Default>
</Literal>
<Literal>
<ID>VarName</ID>
<Tooltip>Variable Name</Tooltip>
<Default>MyProperty</Default>
</Literal>
</Declarations>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Fluent Snippet Builder
I have creates a fluent class that will build up the XML code snippet element. It is designed to run in LINQPad. The fluent class has the following methods for creating a code snippet
- SetHeaderAuthorAndTitle(author, title)
- SetHeaderDescriptionAndHelpUrl(description, optional helpUrl)
- SetHeaderShortcut(shortcut)
- SetSnippetLanguage(enum) - Optional defaults to CSharp
- AddLiteralValue(id, tooltip, defaultValue) - Call for each variable (literal) you want to add to the template
- GetSnippetTemplate - returns the XML for the Code Snippet (you can then add your template into the CData section)
Calling Fluent Snippet Builder
Using LINQPad this is the code to create the Snippet above.
void Main()
{
var fsb = new FluentSnippetBuilder()
.SetHeaderAuthorAndTitle("Jonathan Purdom","Serialiazable Property")
.SetHeaderDescriptionAndHelpUrl("Public Property with XML and JSON Serializable Attributes")
.SetHeaderShortcut("propjx")
.AddLiteralValue("XmlName","XML Element Name of the Property","name")
.AddLiteralValue("JsonName","Json Element Name of the Property","name")
.AddLiteralValue("VarType","Property type","string")
.AddLiteralValue("VarName","Variable Name","MyProperty");
var output = fsb.GetSnippetTemplate();
Console.WriteLine(output);
}
Fluent and Serializable Classes
These are the classes used to create the Code Snippet
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
#region Fluent Class
public class FluentSnippetBuilder
{
private CodeSnippet _snippet;
private string _delimiter = "$";
public FluentSnippetBuilder()
=> _snippet = new CodeSnippet();
public string GetSnippetTemplate()
{
var literals = _snippet.Snippet.Declerations.Select(c => $"{_delimiter}{c.Id}{_delimiter}").ToList();
var template = new XmlNode[] { new XmlDocument().CreateCDataSection(string.Join("\n",literals))};
_snippet.Snippet.Code.Template = template;
var codeSnippets = new CodeSnippets();
codeSnippets.CodeSnippet = _snippet;
return SerializeSnippet<CodeSnippets>(codeSnippets);
}
#region Populate Header Values
public FluentSnippetBuilder SetHeaderAuthorAndTitle(string author, string title)
{
if (string.IsNullOrWhiteSpace(author)) throw new ArgumentNullException(nameof(author));
if (string.IsNullOrWhiteSpace(title)) throw new ArgumentNullException(nameof(title));
_snippet.Header.Author = author.Trim();
_snippet.Header.Title = title.Trim();
return this;
}
public FluentSnippetBuilder SetHeaderDescriptionAndHelpUrl(string description, string helpUrl = "")
{
if (string.IsNullOrWhiteSpace(description)) throw new ArgumentNullException(nameof(description));
_snippet.Header.Description = description.Trim();
if (!string.IsNullOrWhiteSpace(helpUrl))
_snippet.Header.HelpUrl = helpUrl.Trim();
return this;
}
public FluentSnippetBuilder SetHeaderKeywords(IEnumerable<string> keywords)
{
if (keywords is null) throw new ArgumentNullException(nameof(keywords));
_snippet.Header.KeyWords.AddRange(
keywords.Select(c => new Keyword { Value = c.Trim()}).ToList());
return this;
}
public FluentSnippetBuilder SetHeaderShortcut(string shortCut)
{
if (string.IsNullOrWhiteSpace(shortCut)) throw new ArgumentNullException(nameof(shortCut));
_snippet.Header.ShortCut = shortCut.Trim();
return this;
}
public FluentSnippetBuilder SetHeaderSnippetTypes(IEnumerable<string> snippetTypes)
{
if (snippetTypes is null) throw new ArgumentNullException(nameof(snippetTypes));
_snippet.Header.CodeSnippetTypes.AddRange(
snippetTypes.Select(c => new SnippetType { Value = c.Trim() }).ToList());
return this;
}
#endregion
#region Populate Snippet Values
public FluentSnippetBuilder SetSnippetLanguage(LanguageValue language)
{
_snippet.Snippet.Code.Language = language.ToString();
return this;
}
public FluentSnippetBuilder ChangeDelimiterFromDefault(string delimiter)
{
if (string.IsNullOrWhiteSpace(delimiter)) throw new ArgumentNullException(delimiter);
_delimiter = delimiter.Trim();
_snippet.Snippet.Code.Delimiter = _delimiter;
return this;
}
public FluentSnippetBuilder AddLiteralValue(string id, string tooltip, string defaultValue)
{
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentNullException(nameof(id));
if (string.IsNullOrWhiteSpace(tooltip)) throw new ArgumentNullException(nameof(tooltip));
if (string.IsNullOrWhiteSpace(defaultValue)) throw new ArgumentNullException(nameof(defaultValue));
_snippet.Snippet.Declerations.Add(new Literal {Id = id, ToolTip = tooltip, DefaultValue = defaultValue});
return this;
}
#endregion
#region Private Methods
private string SerializeSnippet<T>(T value)
{
var serializer = new XmlSerializer(typeof(T));
using var writer = new StringWriter();
serializer.Serialize(writer, value);
return writer.ToString();
}
#endregion
}
#endregion Fluent Class
#region Serializeable Classes
[XmlRoot(Namespace = "http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet")]
public class CodeSnippets
{
[XmlElement("CodeSnippet")]
public CodeSnippet CodeSnippet { get; set; } = new CodeSnippet();
}
public class CodeSnippet
{
[XmlAttribute("Format")]
public string Format { get; set; } = "1.0.0";
[XmlElement("Header")]
public CodeSnippetHeader Header {get;set;} = new CodeSnippetHeader();
[XmlElement("Snippet")]
public Snippet Snippet{ get; set; } = new Snippet();
}
public class CodeSnippetHeader
{
[XmlElement("Title")]
public string Title { get; set; }
[XmlElement("Author")]
public string Author { get; set; }
[XmlElement("Description")]
public string Description { get; set; }
[XmlElement("HelpUrl")]
public string HelpUrl { get; set; }
[XmlArrayAttribute("CodeSnippetTypes")]
public List<SnippetType> CodeSnippetTypes { get; set; } = new List<SnippetType>();
[XmlArrayAttribute("KeyWords")]
public List<Keyword> KeyWords {get;set;} = new List<Keyword>();
[XmlElement("ShortCut")]
public string ShortCut {get;set;}
}
public class Snippet
{
[XmlElement("Code")]
public Code Code {get;set;} = new Code();
[XmlArray("Declarations")]
public List<Literal> Declerations {get;set;} = new List<Literal>();
}
public class Literal
{
[XmlElement("ID")]
public string Id { get; set; }
[XmlElement("Tooltip")]
public string ToolTip { get; set; }
[XmlElement("Default")]
public string DefaultValue {get;set;}
}
public class Code
{
[XmlAttribute("Language")]
public string Language { get; set; } = "CSharp";
[XmlAttribute("Delimiter")]
public string Delimiter { get; set;} = "$";
[XmlText]
public XmlNode[] Template {get;set;}
public Code()
{
Template = new XmlNode[] { new XmlDocument().CreateCDataSection("")};
}
}
public class SnippetType
{
[XmlText]
public string Value { get; set; }
}
public class Keyword
{
[XmlText]
public string Value { get; set; }
}
#endregion
#region Enum Values
public enum LanguageValue
{
VB,
CSharp,
CPP,
XAML,
XML,
JavaScript,
TypeScript,
SQL,
HTML
}
#endregion
Post Running Script
When you run the script with the literal values it will create the CData section with the literal values in only. At that point you then need to replace the code around the variable values. Final step is to save it as an XML documnent.
Output from Fluent Builder
<Code Language="CSharp" Delimiter="$"><![CDATA[$XmlName$
$JsonName$
$VarType$
$VarName$]]></Code>
Output with Code Template
So in the code template we have added the attributes that we want to code generate and then placed the literals that we want to replace when the snippet is activated
<Code Language="CSharp" Delimiter="$"><![CDATA[
[XmlElement("$XmlName$")]
[JsonProperty("$JsonName$")]
public $VarType$ $VarName$ {get; set;}
]]></Code>
Installing a Code Snippet
In Visual Studio open the Code Snippets Manager (Tools > Code Snippets Manager). Set the language for the code snippet. Click the import button and find the code snippet xml file and then choose where to add the snippet and click Ok to finish