PowerShell: Writing code snippets in the ISE

This is my scratchpad for a simple talk I’ll be doing on creating code snippets for use in the ISE.

Code

PsPresentation01

Code

PsPresentation02.PNG

code

PsPresentation05.PNG

Example with intentional error to show the contrast:

PsPresentation04.PNG

Advertisements

Visual Studio Code: a little more on snippets

Firstly, the think I always forget after being away for a while: you invoke the snippets intellisense by doing CTRL-spacebar, on a PC, anyway.

While blindingly obvious, this is a real time saver…

{
 "Print to console": {
 "prefix": "BasicHtml",
 "body": [
 "<!DOCTYPE html>",
 "<html>",
 "\t<head>",
 "\t\t<title>From the snippets location in Visual Studio Code</title>",
 "\t\t<meta charset=\"utf-8\">",
 "\t\thttp://BasicStuff.js",
 "\t</head>",
 "\t<body>",
 "\t</body>",
 "</html>",
 "<!-- https://validator.w3.org/nu/#textarea -->"
 ],
 "description": "A basic snippet of HTML"
 }
}

VSCode: richer snippets

Summary: I want to be able to use or define a constructor snippet in Visual Studio Code for C#. However it achieves it, that means when I press a reserved (by me, if I’m defining it) set of keystrokes, then it has to determine the class name, which at that point is only in memory? And therefore needs to be reflected over? In fact the last question is supposition and pointless.. but it’s staying there. 🙂 Regardless, I feel a bit frustrated that I have burned 3 hours and got nowhere. Diddums.

Like the ones in fullfat Visual Studio, that’s what I need. e.g. ctor. Means nothing in VSCode evidently.
This is the ctor snippet in VS-speak:

 

snippetvs01

I thought I read that VSCode supported a richer syntax, so time to put that to the test.

Firstly there is this link:

snippetvs02

Not a lot right now then. And what is there just uses the lean syntax I have used before. So no use.

Then we have the tutorial here, which contains this promising thang…

So first try

npm install -g yo

Which in my case gives me:

Your npm version is outdated.

Upgrade to the latest version by running:
npm install -g npm

OK…Getting annoyed now:

snippetvs04

Maybe it was my own fault for doing it through PowerShell – now trying cmd/Node:

snippetvs05

snippetvs06

Seems better:

snippetvs07

Now try
npm install -g generator-code

snippetvs08

Now

yo code

snippetvs09

snippetvs10

Do a find for folders with .tmSnippet…

snippetvs11

C:\Users\Dennis\AppData\Roaming\npm\node_modules\generator-code\test\fixtures\tmsnippets

Plug that in:

snippetvs12

snippetvs13

snippetvs14

Really unhelpful. Exactly where have you installed it? Can’t find it using Windows Search, so let’s try PowerShell:

gci -Recurse -Path . -Filter “vsc-extension-quickstart.md” -ErrorAction SilentlyContinue

snippetvs15

snippetvs16

snippetvs17
# Welcome to your VS Code Extension

## What’s in the folder
* This folder contains all of the files necessary for your extension
* `package.json` – this is the manifest file that defines the location of the snippet file
and specifies the language of the snippets
* `snippets/snippets.json` – the file containing all snippets

## Get up and running straight away
* press `F5` to open a new window with your extension loaded
* create a new file with a file name suffix matching your language
* verify that your snippets are proposed on intellisense

## Make changes
* you can relaunch the extension from the debug toolbar after making changes to the files listed above
* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes

## Install your extension
* To start using your extension with Visual Studio Code copy it into the /.vscode/extensions folder and restart Code.
* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension.

https://code.visualstudio.com/docs/tools/yocode

https://code.visualstudio.com/docs/tools/samples

 

https://code.visualstudio.com/docs/editor/extension-gallery

Let’s try this route for an example:

snippetvs18

 

snippetvs19

So the C# XML docs extension clearly works… I just have to work out how now, and then I can use that for ctor. Another night:

snippetvs21

In fact as is often the way, now I am out of time, I’ve found possibly the most useful entries:

https://code.visualstudio.com/docs/customization/userdefinedsnippets

https://code.visualstudio.com/docs/editor/editingevolved

 

 

 

VSCode snippets: project.json

What handier snippet is there in the World of asp vNext than project.json:

 "Basic project.json file": {
 "prefix": "PJson",
 "body": [
 "{",
 "\t\"dependencies\": {",
 
 "\t},",
 "\t\"commands\": {",
 "\t\t\"run\": \"run\"",
 "\t},",
 "\t\"frameworks\": {",
 "\t\t\"dnx451\": {},", 
 "\t\t\"dnxcore50\": {",
 "\t\t\t\"dependencies\": {",
 "\t\t\t\t\"System.Console\": \"4.0.0-beta-23516\"",
 "\t\t\t}", 
 "\t\t}", 
 "\t}",
 "}"
 ], 
 "description": "Basic project.json file template"
 }

Code

ProjectJson01
Resulting code:
ProjectJson02

Trivial addition until/unless it can be done properly in VSCode:
CodeSnippetsinVsCode13

"Code docs": {
 "prefix": "codeDocs",
 "body": [
 "/// <summary>",
 "/// This class performs an important function.",
 "/// </summary>"
 ],
 "description": "Bit of a clunky way to create Docs"
 }


Visual Studio Code: code snippets

Found this (admittedly staring me in the face right now… but it wasn’t a while back, I’m sure)

So looking in VSCode:

So lest I make a mess of it, quick back up of the text:

{
// Place your snippets for C# here. Each snippet is defined under a snippet name and has a prefix, body and
// description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, ${id} and ${id:label} and ${1:label} for variables. Variables with the same id are connected.
// Example:
"Print to console": {
"prefix": "log",
"body": [
"console.log('$1');",
"$2"
],
"description": "Log output to console"
}

Then add a simple property snippet, using a slightly different shortcut than [prop], as that automatically gets picked up, I’ll find out how another time.

You don’t need to restart VSCode to get this working. It updates on the fly. The ONE thing this lacks right now is the ReSharper type macro language that is very useful for doing the private backing field/public property thing (I know what I mean 🙂 )

"Simple Property by Dennis": {
"prefix": "propdennis",
"body": [
"public ${type:string} ${name} {get;set;}"
],
"description": "Create a Dennis simple property"
}


In fact I will show you exactly what I mean...

My standards say that a private variable starts with an underscore followed by a lower case character, as shown above. However, the best that I can achieve with this snippet manager, with the minimum number of keystrokes, is this, because unlike ReSharper, there is no macro language or mechanism to achieve the above, so I get this:

CodeSnippetsinVsCode11

And this is how you achieve the immediately above anyway:

CodeSnippetsinVsCode12

In the docs, they talk about using $1 etc to represent tab-stops, but that doesn’t work for me, and they have not included a working example. This is good enough:

"Simple Property by Dennis": {
"prefix": "propWithBacker",
"body": [
"\t\tprivate ${type:string} _${name};\n",
"\t\tpublic ${type:string} ${name} {",
"\t\t\tget {",
"\t\t\t\treturn _${name};",
"\t\t\t}",
"\t\t\tset { ",
"\t\t\t\t_${name} = ${name};",
"\t\t\t}",
"\t\t}\n"
],
"description": "Create a property with backing field"
}

Hm, actually you don’t need that string of tabs. The snippet placement is sensitive to the position of your cursor, so this will do:

"Simple Property by Dennis": {
"prefix": "propWithBacker",
"body": [
"private ${type:string} _${name};\n",
"public ${type:string} ${name} {",
"\tget {",
"\t\treturn _${name};",
"\t}",
"\tset { ",
"\t\t_${name} = ${name};",
"\t}",
"}\n"
],
"description": "Create a property with backing field"
}


May 2016...
I didn't want to start a new page, but this is a good starting set of Angular and BootStrap er things, to get started (in Emmet and VSC - this goes in the html.json page)

{
 "Basic HTML boilerplate": {
 "prefix": "BasicHtml",
 "body": [
 "<!DOCTYPE html>",
 "<html>",
 "\t<head>",
 "\t\t<title>From the snippets location in Visual Studio Code</title>",
 "\t\t<meta charset=\"utf-8\">",
 "\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
 "\t\thttps://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.jsx",
 "\t\t<link rel=\"stylesheet\" href=\"http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css\"></link>",
 "\t\thttp://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js",
 "\t\thttp://BasicStuff.js",
 "\t</head>",
 "\t<body>",
 "\t\t<input type=\"button\" value=\"Press me\" id=\"BasicHtmlPressMeId\">",
 "\t\t<input type=\"text\" value=\"Press mex\" id=\"BasicHtmlCapacityId\">",
 "\t</body>",
 "</html>",
 "<!-- https://validator.w3.org/nu/#textarea -->",
 "<!-- Location is [file:///e:/sandbox/javascript/test.html] -->"
 ],
 "description": "A basic snippet of HTML"
 },
 "BSButtons": {
 "prefix": "B01",
 "body": [
 "
“, “\t

A bunch of big Bootstrap buttons

“, ”

“, “<p>”, “\t<button type=\”button\” class=\”btn btn-lg btn-default\”>Default</button>”, “\t<button type=\”button\” class=\”btn btn-lg btn-primary\”>Primary</button>”, “\t<button type=\”button\” class=\”btn btn-lg btn-success\”>Success</button>”, “\t<button type=\”button\” class=\”btn btn-lg btn-info\”>Info</button>”, “\t<button type=\”button\” class=\”btn btn-lg btn-warning\”>Warning</button>”, “\t<button type=\”button\” class=\”btn btn-lg btn-danger\”>Danger</button>”, “</p>” ], “description”: “Initial bootstrap buttons” }, “Ang01”: { “prefix”: “Ang01”, “body”: [ “\thttps://ajax.googleapis.com/ajax/libs/angularjs/1.5.1/angular.js” ], “description”: “Angular CDN” }, “BSScriptTags”: { “prefix”: “BSScript”, “body”: [ “\t\thttps://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.jsx“, “\t\t<link rel=\”stylesheet\” href=\”http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css\”></link>”, “\t\thttp://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js” ], “description”: “The Bootstrap src, css and jquery bits” } }

Visual Studio… Code Snippets: no snippets manager?

.., no problem:

Keeping things simple, this is a basic snippet, that can act as a template. It has no parameters:

BasicSnippet01

You will see that the Title and the ShortCut, and the filename, are all [MyTestMethodEmpty]. That helps with cataloguing and maintenance, I find.

So that is sitting on the file system in some scratch location, and we want to use it in Visual Studio.

These are the steps:

In Visual Studio, import the snippet you created earlier using any-old text editor (note that even though I intend to use this in C#, I have left the language at the default of Basic):

BasicSnippet02

Browse to the location where you created/saved your snippet, and click [Open] ([Open] not shown in the screenshot):

BasicSnippet03

The next dialog carries forward the named snippet, and suggests a location: this might be a default, or it might just be remembering my previous choice. I don’t care how it decided, this is fine for my purposes:

BasicSnippet04

We then click [Finish]/[OK] (not shown here), and we return to the IDE.

There are 2 things to check: a) Does Visual Studio find the snippet, and b) where has it stored it?

a) Does Visual Studio find the snippet

In the IDE, I start to type [myt], and it finds the shortcut:

BasicSnippet05

I then press tab twice… and it brings up the boilerplate that is clearly from the snippet I created (the squiggles are because I did the editing in a non-Test class). So this is clearly working:

BasicSnippet06

b) where has it stored it?

I just don’t trust Windows Search (and that continues into Windows 10), so I use PowerShell to look for the wild card. Although it’s not efficient, on an SSD it goes pretty quickly. And this screenshot confirms on the file system what we asked for in the IDE, in that we created the file c:\temp, and Visual Studio must have followed the instruction to put in the [My Code Snippets folder].

BasicSnippet07

gci -Path c:\ -Filter *empty*.snippet -Recurse -File -ErrorAction SilentlyContinue

Backup

As this are all pretty small, it is valid to take the whole content of the Code Snippets parent folder, and back that up – 25 files looks a bit suspiciously small, so maybe validate that before putting faith in the backup, but you get my point regarding size:

BasicSnippet08

Blue below represents the things you would change in a template without parameters:

<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>MyTestMethodEmpty</Title>
<Author>Dennis</Author>
<Description>MyTestMethodEmpty</Description>
<HelpUrl></HelpUrl>
<SnippetTypes />
<Keywords />
<Shortcut>MyTestMethodEmpty</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”csharp” Kind=”” Delimiter=”$”><![CDATA[[TestMethod]
public void TestMethod() {
Assert.Fail();
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>

Snippet set

Untidy, but let’s just get a few in one place:

<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>Email</Title>
<Author>Dennis</Author>
<Description>Email</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyEmail</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”csharp” Kind=”” Delimiter=”$”><![CDATA[namespace FootballClub.Model {
public class Email {

public string EmailAddress { get; set; }
}
}
]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>


<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>Address</Title>
<Author>Dennis</Author>
<Description>Address</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyAddress</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”csharp” Kind=”” Delimiter=”$”><![CDATA[namespace FootballClub.Model {
public class Address {

public string FirstLine { get; set; }
public string City { get; set; }
public string County { get; set; }
public string Country { get; set; }
public string PostCode { get; set; }
}
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>


<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>Club</Title>
<Author>Dennis</Author>
<Description>Club</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyClub</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”csharp” Kind=”” Delimiter=”$”><![CDATA[using System;
using System.Collections.Generic;

namespace FootballClub.Model {
public class Club {

public int Id { get; set; }
public string Name { get; set; }
public DateTime DateOfIncorporation { get; set; }
public Address Address { get; set; }
public List<Email> Emails { get; set; }
}
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>


<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>BasicTests</Title>
<Author>Dennis</Author>
<Description>BasicTests</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyTestsBasic</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”csharp” Kind=”” Delimiter=”$”><![CDATA[using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FootballClub.Tests {
[TestClass]
public class CoreClubTests {

Model.Club _club = new Model.Club {
Id = 101,
Name = “Tottenham”,
Address = new Model.Address { FirstLine = “99 White Hart Lane” },
DateOfIncorporation = new DateTime(1899, 5, 6)
};

[TestMethod]
public void ModelPropertiesAreUpdatedViaModelFacade() {
var modelFacade = new ClubFacade(_club);
modelFacade.Name = “Arsenal”;
Assert.AreEqual(“Arsenal”, _club.Name);

}

[TestMethod]
public void ModelPropertiesAreGotViaModelFacade() {
var modelFacade = new ClubFacade(_club);
Assert.AreEqual(_club.Id, modelFacade.Id);
}

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ModelFacadeDoesNotAcceptNullClubModel() {
try {
var modelFacade = new ClubFacade(null);
} catch(ArgumentNullException ex) {
if (ex.ParamName != “Model is null: not allowed”) {
Assert.Fail(“Expected exception message not found. Found: ” + ex.ParamName);
}
throw ex;
}
}
}
}
]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>


<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>ClubFacade</Title>
<Author>Dennis</Author>
<Description>ClubFacade</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyClubFacade</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”csharp” Kind=”” Delimiter=”$”><![CDATA[namespace FootballClub.Tests {
public class ClubFacade : Facade.Observer<Model.Club> {
public ClubFacade(Model.Club Club) : base(Club) {}

public int Id {
get { return GetValue<int>(); }
set { SetValue(value);}
}

public string Name {
get { return GetValue<string>(); }
set { SetValue(value);}
}
}
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>


<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>Observer</Title>
<Author>Dennis</Author>
<Description>Observer</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyObserver</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”VB” Kind=”” Delimiter=”$”><![CDATA[using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace FootballClub.Facade {
public class Observer<TModel> : INotifyPropertyChanged {

protected readonly TModel _model;
public Observer(TModel Model) {
if (Model == null) {
throw new ArgumentNullException(“Model is null: not allowed”);
}
_model = Model;
}

protected void OnPropertyChanged([CallerMemberName] string caller = null) {
var x = PropertyChanged;
if (x != null) {
x(this, new PropertyChangedEventArgs(caller));
}
}

public event PropertyChangedEventHandler PropertyChanged;

protected void SetValue<TValue>(TValue value, [CallerMemberName] string propertyName = null) {
var propertyInfo = _model.GetType().GetProperty(propertyName);
var currentValue = propertyInfo.GetValue(_model);
if (!Equals(currentValue, value)) {
propertyInfo.SetValue(_model, value);
OnPropertyChanged(propertyName);
}
}

protected TValue GetValue<TValue>([CallerMemberName] string propertyName = null) {
var propertyInfo = _model.GetType().GetProperty(propertyName);
return (TValue)propertyInfo.GetValue(_model);
}
}
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>


<?xml version=”1.0″ encoding=”utf-8″?>
<CodeSnippets xmlns=”http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet”&gt;
<CodeSnippet Format=”1.0.0″>
<Header>
<Title>INPC tests</Title>
<Author>Dennis</Author>
<Description>INPC tests</Description>
<HelpUrl />
<SnippetTypes />
<Keywords />
<Shortcut>MyTestInpc</Shortcut>
</Header>
<Snippet>
<References />
<Imports />
<Declarations />
<Code Language=”VB” Kind=”” Delimiter=”$”><![CDATA[using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace FootballClub.Tests {
[TestClass]
public class CoreClubTests {

Model.Club _club = new Model.Club {
Id = 101,
Name = “Tottenham”,
Address = new Model.Address { FirstLine = “99 White Hart Lane” },
DateOfIncorporation = new DateTime(1899, 5, 6)
};

[TestMethod]
public void ModelPropertiesAreUpdatedViaModelFacade() {
var modelFacade = new ClubFacade(_club);
modelFacade.Name = “Arsenal”;
Assert.AreEqual(“Arsenal”, _club.Name);

}

[TestMethod]
public void ModelPropertiesAreGotViaModelFacade() {
var modelFacade = new ClubFacade(_club);
Assert.AreEqual(_club.Id, modelFacade.Id);
}

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ModelFacadeDoesNotAcceptNullClubModel() {
try {
var modelFacade = new ClubFacade(null);
} catch(ArgumentNullException ex) {
if (ex.ParamName != “Model is null: not allowed”) {
Assert.Fail(“Expected exception message not found. Found: ” + ex.ParamName);
}
throw ex;
}
}
}
}]]></Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>