{"id":78021,"date":"2018-04-06T18:52:27","date_gmt":"2018-04-06T18:52:27","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=78021"},"modified":"2021-06-03T16:46:58","modified_gmt":"2021-06-03T16:46:58","slug":"writing-vs-code-extensions-typescript","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/development\/dotnet-development\/writing-vs-code-extensions-typescript\/","title":{"rendered":"Writing VS Code Extensions with TypeScript"},"content":{"rendered":"<p>In this article, you will learn how to create a Visual Studio Code (VS Code) extension using TypeScript. VS Code is a cross platform lightweight development environment from Microsoft. The article begins with a high-level overview of each of the core components needed and walks through how to get a development environment set up, a template configured, and how to write some code to get started. VS Code extensions enable you to easily extend the VS Code development environment to meet whatever needs you have.<\/p>\n<h2>What is TypeScript?<\/h2>\n<p>TypeScript is a superset of JavaScript. It compiles to JavaScript so the output from the compile process is plain JavaScript. It checks for types, etc., when TypeScript is compiled rather than when the code is executed. TypeScript can be any valid JavaScript but if it is any TypeScript then it must be compiled to JavaScript, hence JavaScript is valid TypeScript, but TypeScript is not valid JavaScript.<\/p>\n<h2>Why TypeScript?<\/h2>\n<p>TypeScript compiles to JavaScript. It provides a safe environment for writing large applications as well as to scale up small applications written in JavaScript. JavaScript does not support any \u2018type checking\u2019 or \u2018type definitions\u2019 for 3rd party components and hides bugs until runtime. In short, TypeScript is safer than JavaScript. If you just have JavaScript, then you do not have any type checking or type definitions for 3rd party components. Scaling up from a very simple small application is hard with JavaScript and safer with TypeScript. You could say that using JavaScript for larger projects is hard because of the dynamic nature of JavaScript types &#8211; the thing that makes it so powerful also holds it back by hiding bugs until runtime.<\/p>\n<h2>What are TypeScript Typings?<\/h2>\n<p>TypeScript typings are the type information that the TypeScript compiler needs to identify JavaScript libraries correctly. Without the ability to add \u2018Type Information\u2019 to an existing package, it would be difficult to validate the code of any object from a package. It holds true for your own existing JavaScript package as well as for a public one.<\/p>\n<h2>Dependency Management<\/h2>\n<p>VS Code is an application that runs on electron which is a mixture of Chromium, Google&#8217;s open source version of Chrome and <em>node.js<\/em>. If you want to make use of any dependencies, then you will use npm which is the \u2018node package manager\u2019. Npm is like NuGet but for JavaScript packages. Npm contains packages in JavaScript as well as TypeScript. As you are going to be writing in TypeScript, you can use either plain JavaScript packages or JavaScript packages with the TypeScript typings. Typically, packages are published in plain JavaScript no matter which language they are written in with any corresponding typings that TypeScript needs published to a GitHub repo called DefinetlyTyped. The DefinetlyTyped repo has, as of today, over 4000 sets of type information for packages you can use in <a href=\"https:\/\/github.com\/DefinitelyTyped\/DefinitelyTyped\/tree\/master\/types\">TypeScript<\/a>.<\/p>\n<h2>What Types of Extensions Can Be Run?<\/h2>\n<p>There are three main types of extensions that you can write for VS Code:<\/p>\n<ul>\n<li>Extensions<\/li>\n<li>Language Server<\/li>\n<li>Debug Adapter<\/li>\n<\/ul>\n<p>An extension allows you to add a window or a command that lets you take some action. A language server allows you to provide language information to VS Code, such as IntelliSense for C#. A debug adapter allows you to use the VS Code debugger UI to debug different types of applications, for example, PowerShell or C#. In this article, you will learn how to write an extension.<\/p>\n<h2>Writing an Extension<\/h2>\n<p>The first thing to do is to create the project skeleton. You will use a tool to create the default files and configuration which will let you press <strong>F5<\/strong> in VS Code and start a development instance with the extension loaded. There is a tool that runs on <em>node.js<\/em> called the \u2018Yeoman generator\u2019. Yeomon has a template to create a sample VS Code extension project. To get this to run you first need <em>node.js, <\/em>npm, and VS Code install those together from <a href=\"https:\/\/nodejs.org\/en\/\">here<\/a>.<\/p>\n<p>The installer for <em>node.js<\/em> includes npm, and then, if you need VS Code, you can download it from <a href=\"https:\/\/code.visualstudio.com\/download\">here<\/a>.<\/p>\n<p>Once <em>node.js<\/em> and npm are installed, to validate that they are installed correctly, open a command prompt and run:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">npm --version<\/pre>\n<p>If that gives you a version number, then you can use npm to install the Yeoman generator by running:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">npm install -g yo generator-code<\/pre>\n<p>This means npm will download and install the two packages, <strong>yo<\/strong> and <strong>generator-code<\/strong> the <strong>-g<\/strong> option means install it globally to the machine.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"980\" height=\"483\" class=\"wp-image-78022\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-38.png\" \/><\/p>\n<p>Installing the <strong>yo<\/strong> generator globally means you can execute it at a command prompt using:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">yo code<\/pre>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"752\" height=\"412\" class=\"wp-image-78023\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-16.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\0-yoman.png\" \/><\/p>\n<p>Follow the prompts to create a new extension using:<\/p>\n<p>&#8211; New Extension (TypeScript)<\/p>\n<p>&#8211; Give your extension a name<\/p>\n<p>&#8211; Accept the default for the remaining options. You can put in a dummy publisher name if you do not have one, and you will see how to get the name later on.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-78047\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/TS1.png\" alt=\"\" width=\"746\" height=\"432\" \/><\/p>\n<p>This will create a barebones extension and run the npm install which downloads all the required dependencies for you. When the npm install finishes, you can type <strong>code .<\/strong> into the prompt and it will start VS Code in the correct place (Note if you have just installed VS Code you will need to restart to get the correct path settings configured for <strong>code .<\/strong> to work):<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-78025\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-40.png\" width=\"749\" height=\"323\" \/><\/p>\n<p>When you open the <em>package.json<\/em> file in VS Code, you will see that this contains the information that <em>npm<\/em> needs to download the correct dependencies and to build the project, as well as the information required by VS Code to create the extension:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"712\" height=\"720\" class=\"wp-image-78026\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-41.png\" \/><\/p>\n<p>The top section surrounded by the green box is the information required by the VS Code extension packager to create a package that can be shared and loaded in VS Code. Ignore this for the moment and look at the second section, surrounded by the red box.<\/p>\n<p>There are two sections: <strong>Scripts<\/strong> and <strong>devDependencies<\/strong>, though in real-life scenarios there would be one more dependency called <strong>dependencies<\/strong>. The <strong>devDependencies<\/strong> are the dependencies that are required to build the project, as opposed to the dependencies required to run the project. Typically, the <strong>devDependencies<\/strong> will have several more dependencies than the production dependencies, and when you deploy without the <strong>devDepenedencies<\/strong>, the size of the deployment becomes a lot smaller.<\/p>\n<p>Here you can see that you have <strong>devDependencies<\/strong> on typescript, VS Code which are packages, and then <strong>typings<\/strong> for <strong>node<\/strong> and <strong>mocha<\/strong>. You can tell the difference because <strong>node<\/strong> and <strong>mocha<\/strong> begin with <strong>@types\/<\/strong>. If you look in your node_modules folder (please don&#8217;t be scared by the number of dependencies), you will see that you have a <strong>@types\/mocha<\/strong> and a <strong>mocha<\/strong>. The <strong>@types\/mocha<\/strong> is the typing information for the package <strong>mocha<\/strong>. You don&#8217;t need to specify mocha in the <em>package.json<\/em> file as it is included by the reference to VS Code. If you want to explicitly include <strong>mocha<\/strong>, then you could run:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">npm install mocha --save-dev<\/pre>\n<p>If it was a production dependency required at runtime, you could run:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">npm install mocha \u2013save<\/pre>\n<p>These commands will both update the <em>package.json<\/em> and install <strong>mocha<\/strong>, if required.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"516\" height=\"220\" class=\"wp-image-78027\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-17.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\3-adding-mocha-dependency.png\" \/><\/p>\n<p>The <strong>scripts<\/strong> section contains tasks that npm can run such as compiling the TypeScript code into JavaScript or running the unit tests.<\/p>\n<p>In the scripts section, there are a couple of useful tasks which you can run such as <strong>watch<\/strong> which runs in the background. While <strong>watch<\/strong> is running, as you save your TypeScript files, the watcher calls compile so you don&#8217;t need to press <strong>CTRL+B<\/strong> every time you want to build the project.<\/p>\n<p>While in VS Code, you press <strong>CTRL+SHIFT+P<\/strong> (or whatever it is on your operating system) to bring up the command palette and choose <em>Tasks: Run Task<\/em> followed by <em>npm:watch.<\/em><\/p>\n<p>It will start the watcher in the background. To see it actually running, re-open the command palette and select <em>Tasks: Show running tasks<\/em> and choose <em>npm:watch<\/em>. You will see the terminal window and, hopefully, it should say <em>Compilation complete<\/em> or something similar:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"549\" height=\"176\" class=\"wp-image-78028\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-42.png\" \/><\/p>\n<p>If you decided you no longer wanted the watch task you previously ran, you could open the command palette and choose <em>Tasks: Terminate Task<\/em>. VS Code provides a nice way to run these npm tasks, but what I personally find useful is to split the screen into VS Code and either one or two background PowerShell windows running tasks. Then, perhaps run the build watcher in one and some tests in another window rather than keeping everything inside VS Code such as:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1382\" height=\"729\" class=\"wp-image-78029\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-43.png\" \/><\/p>\n<p>Whether you choose to use VS Code as your task runner or use a separate window, you get quite a bit of flexibility either way.<\/p>\n<p>If you now go back to the top section of the <em>package.json<\/em> file, you have:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  \"name\": \"calculator\",\r\n    \"displayName\": \"calculator\",\r\n    \"description\": \"\",\r\n    \"version\": \"0.0.1\",\r\n    \"publisher\": \"Ed-Elliott\",\r\n    \"engines\": {\r\n      \"VS Code\": \"^1.19.0\"\r\n    },\r\n    \"categories\": [\r\n      \"Other\"\r\n    ],\r\n    \"activationEvents\": [\r\n      \"onCommand:extension.sayHello\"\r\n    ],\r\n    \"main\": \".\/out\/extension\",\r\n    \"contributes\": {\r\n      \"commands\": [\r\n        {\r\n          \"command\": \"extension.sayHello\",\r\n          \"title\": \"Hello World\"\r\n        }\r\n      ]\r\n    }<\/pre>\n<p>The <strong>name<\/strong>, <strong>displayName<\/strong>, <strong>description<\/strong>, and <strong>version<\/strong> should be self-explanatory. The <strong>publisher<\/strong> is the unique name of your VSTS publisher. VS Code uses VSTS to host its extensions so you need a free VSTS account established. My publisher name is <em>Ed-Elliott,<\/em> so you will need to use your own VSTS publisher account. If you are not sure about your publisher name, go to the <a href=\"https:\/\/marketplace.visualstudio.com\/manage\/publishers\/\">Marketplace<\/a>.<\/p>\n<p>Login using your VSTS account. If you don&#8217;t have a VSTS account, go to my.visualstudio.com and signup for Dev Essentials. Once you are logged in, you click <em>Create New Publisher<\/em>. Later, when you publish the extension, you will need to logon to VSTS and generate a <em>PAT<\/em> which gives you access to publish, so do make sure you have a VSTS account with the email address associated with the publisher name.<\/p>\n<p>The <strong>engines<\/strong> section states which versions of VS Code that your extension supports. I don&#8217;t see any reason not to choose the latest as the upgrade is so simple, but you could change it here.<\/p>\n<p>The<strong> categories<\/strong> property lets you configure the type of extension. In this case, choose <strong>Other<\/strong>, but it could be one of:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">[Languages, Snippets, Linters, Themes, Debuggers, Formatters, Keymaps, Other, Extension Packs]<\/pre>\n<p>The <strong>activationEvents<\/strong> section lists all the things that should cause VS Code to load the extension. By default, VS Code will only load an extension when something happens to warrant it. If you have no PowerShell code, then you don&#8217;t get the PowerShell extension, for example. The <strong>activationEvent<\/strong> added by <strong>yo code<\/strong> is <strong>:extension.sayHello<\/strong>. The first part, <strong>onCommand<\/strong>, means that when someone opens the command palette and runs the command <strong>helloWorld<\/strong>, the extension is called for the first time in that instance of vs code.<\/p>\n<p>The <strong>main<\/strong> property tells VS Code where to find the extension, in this case, <strong>.\/out\/extension<\/strong>. What this means is that you will need to have a JavaScript file in the directory <strong>out<\/strong> called <em>extension.js<\/em>. If you have run the watcher task or the build task, then you should indeed have that as the <em>ts<\/em> file will be compiled into the <em>.js<\/em> file. The <em>.js<\/em> file should also export a function called <strong>activate<\/strong>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1382\" height=\"729\" class=\"wp-image-78030\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-18.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\6-extension-js.png\" \/><\/p>\n<p>Back in the <em>package.json<\/em> file, you will see the <strong>contributes<\/strong> section. In this example, there is just a single command.:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  \"contributes\": {\r\n      \"commands\": [\r\n        {\r\n          \"command\": \"extension.sayHello\",\r\n          \"title\": \"Hello World\"\r\n        }\r\n      ]\r\n    },<\/pre>\n<p>This has a single command here, but you could contribute multiple commands or something else such as a window. This is an example I used to create a window in another extension I wrote for VS Code:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\"contributes\": {\r\n      \"commands\": [\r\n        {\r\n          \"command\": \"armJsonOutline.showGraph\",\r\n          \"title\": \"ARM: Show Dependency Graph\",\r\n          \"when\": \"resourceLangId == 'json'\"\r\n        },\r\n        {\r\n          \"command\": \"armJsonOutline.openArmOutline\",\r\n          \"title\": \"ARM: Start Outliner\",\r\n          \"when\": \"resourceLangId == 'json'\"\r\n        },\r\n        {\r\n          \"command\": \"armJsonOutline.stopArmOutline\",\r\n          \"title\": \"ARM: Stop Outliner\"\r\n        },\r\n        {\r\n          \"command\": \"armJsonOutline.testExpression\",\r\n          \"title\": \"ARM: Test Expression\"\r\n        }\r\n      ],\r\n      \"views\": {\r\n        \"explorer\": [\r\n          {\r\n            \"id\": \"armJsonOutline\",\r\n            \"name\": \"ARM Template Outline\",\r\n            \"when\": \"resourceLangId == 'json'\"\r\n          }\r\n        ]\r\n      },\r\n      \"menus\": {}\r\n    },<\/pre>\n<p>The things to notice here are that the commands and views should only be visible when a JSON document is open, so, as well as the command definitions, I have also included a <strong>when<\/strong> property. This really contributes to the goal of having VS Code as lightweight as possible; if you don&#8217;t need something then, it isn&#8217;t loaded. This is a great benefit over Visual Studio where everything is loaded unless you explicitly disable it.<\/p>\n<p>Now that the <em>package.json<\/em> file has been discussed, you can explore the sample application. If you open the file <em>src\/extension.ts<\/em> you will can see you first glimpse of TypeScript code:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1060\" height=\"763\" class=\"wp-image-78031\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-44.png\" \/><\/p>\n<p>There is function called <strong>activate<\/strong> which is being exported. This will be executed because in the <em>package.json<\/em> file, you have:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\"main\": \".\/out\/extension\",<\/pre>\n<p>It will be called when someone runs the command <em>sayHello<\/em>. When the <strong>activate<\/strong> function is called, debug line is logged, <strong>console.log<\/strong>, and then it registers a command using:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">VS Code.commands.registerCommand<\/pre>\n<p>This takes two parameters, the first is the name of the command to register. In this case, it is <strong>extension.sayHello<\/strong>. The second parameter is a function that will get executed when the command is run. The sequencing is a little odd here but to clarify what happens:<\/p>\n<ul>\n<li>A user runs a command or event that is in the <strong>activationEvents<\/strong> section in the <em>package.json <\/em>file<em>.<\/em><\/li>\n<li>If the <strong>activate<\/strong> function has not been executed since VS Code was launched, then <strong>activate<\/strong> is called.<\/li>\n<li>The <strong>activate<\/strong> function then registers any commands it would like to and does any initialization work it needs.<\/li>\n<li>If the command the user called is registered by the <strong>activate<\/strong> function, then that command itself is executed.<\/li>\n<\/ul>\n<p>This means you can have multiple commands all registered using the same <strong>activate<\/strong> function without having to pre-register them before the extension is required.<\/p>\n<p>Then when the <strong>activate<\/strong> function returns, the registered handler is executed, because the user originally ran the <strong>extension.sayHello<\/strong> command:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-78048\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/ts2.png\" alt=\"\" width=\"602\" height=\"57\" \/><\/p>\n<p>In the example, the command displays a message saying <em>Hello World!. <\/em>I will change this to <strong>Hello Simple Talk<\/strong> and then press <strong>F5<\/strong> so VS Code should build and run the extension in a development version of VS Code.<\/p>\n<p>Once you start the development version, you can open the command palette and run <em>Hello World<\/em>. When you do that, VS Code calls the <strong>activate<\/strong> function which registers the <strong>extension.sayHello<\/strong> command. When <strong>activate<\/strong> completes, the <strong>sayHello<\/strong> command is executed and displays the message <em>Hello Simple Talk<\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"821\" height=\"150\" class=\"wp-image-78033\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-45.png\" \/><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"522\" height=\"110\" class=\"wp-image-78034\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-46.png\" \/><\/p>\n<h2>Debugging<\/h2>\n<p>Even though the code that gets executed is that compiled from TypeScript to JavaScript, you can still debug the TypeScript using the debugger. You can set a breakpoint in the TypeScript function where you display the message box by either, highlighting the line you are interested in and choosing <em>Toggle breakpoint <\/em>from the <em>Debug<\/em> menu or pressing <strong>F9<\/strong>, or just clicking the space to the left of the line numbers.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"740\" height=\"169\" class=\"wp-image-78035\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-47.png\" \/><\/p>\n<p>When you run the extension under the debugger and execute <em>Hello World<\/em>, the VS Code debugger stops at the breakpoint, and you can do the standard things like evaluating any variables or viewing a stack trace:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone  wp-image-78049\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/ts3.png\" alt=\"\" width=\"825\" height=\"462\" \/><\/p>\n<p>This ability to execute JavaScript code and debug the TypeScript is made possible because, when the TypeScript compiler outputs the JavaScript file, it also outputs a map file which contains all the information the debugger needs to translate the JavaScript locations and objects back to the TypeScript locations and objects:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1003\" height=\"499\" class=\"wp-image-78037\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-20.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\12-javascript-map.png\" \/><\/p>\n<h2>Extending the example and using tests<\/h2>\n<p>So far, the example doesn&#8217;t really do that much, so it\u2019s time to expand the extension to count the number of words in the active document. The first thing to do is to add a new command to <em>package.json<\/em> by adding the <strong>extension.countWords<\/strong> to the <strong>commands<\/strong> array:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\"> \"commands\": [\r\n        {\r\n          \"command\": \"extension.sayHello\",\r\n          \"title\": \"Hello World\"\r\n        },\r\n        {\r\n          \"command\": \"extension.countWords\",\r\n          \"title\": \"Count Words\"\r\n        }\r\n      ]  <\/pre>\n<p>Note, the commands are configured with <strong>extension<\/strong>. This is effectively the namespace. To keep this from clashing with anything else, I normally use the name of the extension instead of <strong>extension<\/strong>. If you are going to publish your extension, you should choose a different prefix.<\/p>\n<p>Once you have a new command, you should also tell VS Code that it should call the <strong>activate<\/strong> function if the new command is called. Otherwise, unless someone happens to run <em>Hello World<\/em>, the command won&#8217;t have been registered, and VS Code will show an error to the user. Update <strong>activationEvents<\/strong> so that it includes the new command:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">  \"activationEvents\": [\r\n      \"onCommand:extension.sayHello\",\r\n      \"onCommand:extension.countWords\"\r\n    ],<\/pre>\n<p>Now, you must implement the command, so go back to the <em>src\/extension.ts<\/em> file. After registering <strong>extension.sayHello<\/strong>, add a new <strong>VS Code.commands.registerCommand<\/strong> for the new command. To test that <em>package.json<\/em> has been set up correctly, just copy and paste <strong>helloWorld<\/strong> (changing the variable name that is returned from <strong>registerCommand<\/strong> so that you can save both of the subscriptions) and test it to make sure that when you call <strong>extension.countWords<\/strong>, you get a message box:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"931\" height=\"471\" class=\"wp-image-78038\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-21.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\13-new-command.png\" \/><\/p>\n<p>Now you know that you have <em>package.json<\/em> set up correctly, you can expand the code to read the current document, count the words, and display the word count:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">    if(vscode.window.activeTextEditor === undefined || \r\n            vscode.window.activeTextEditor.document === undefined){\r\n              return;\r\n          }\r\n          let text = vscoode.window.activeTextEditor.document.getText();\r\n          \r\n          function countWords(str) {\r\n              \/\/from https:\/\/stackoverflow.com\/questions\/18679576\/counting-words-in-string\r\n              return str.trim().split(\/\\s+\/).length;\r\n          }\r\n          vscode.window.showInformationMessage(`Document contains: ${countWords(text)} words`);<\/pre>\n<p>The code will first check to see if there is an active document. If there isn&#8217;t, then it will fail to read the text. If there is an extension that throws an exception, it doesn&#8217;t crash VS Code, but the user gets an error message to say the extension died which isn&#8217;t very nice. By using the <strong>when<\/strong> facility, you could just get the command to show when a document is chosen, but there is still a chance a user could choose the command and then close the active window.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\"commands\": [\r\n        {\r\n          \"command\": \"armJsonOutline.showGraph\",\r\n          \"title\": \"ARM: Show Dependency Graph\",\r\n          \"when\": \"resourceLangId == 'json'\"\r\n        },<\/pre>\n<p>This would still throw an exception, so it\u2019s best to check that it is still valid:<\/p>\n<pre class=\"lang:ps theme:powershell-ise \">        if(VS Code.window.activeTextEditor === undefined || \r\n             VS Code.window.activeTextEditor.document === undefined){\r\n              return;\r\n          }<\/pre>\n<p>The next line grabs a copy of the text from the active window:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">let text = VS Code.window.activeTextEditor.document.getText();<\/pre>\n<p>Once it has the text, it uses a function downloaded from Stack Overflow to count the words:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function countWords(str) {\r\n              \/\/from https:\/\/stackoverflow.com\/questions\/18679576\/counting-words-in-string\r\n              return str.trim().split(\/\\s+\/).length;\r\n          }<\/pre>\n<p>Note that this was straight JavaScript. You could turn it into a safer version if you add type information to it:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">function countWords(str : string) : number {\r\n              \/\/from https:\/\/stackoverflow.com\/questions\/18679576\/counting-words-in-string\r\n              return str.trim().split(\/\\s+\/).length;\r\n          }<\/pre>\n<p>You now know that <strong>str<\/strong> is a string, and the function returns a number. Finally, it shows a message box with the results of calling the <strong>countWords<\/strong> function:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">VS Code.window.showInformationMessage(`Document contains: ${countWords(text)} words`);<\/pre>\n<p>You can use <strong>${}<\/strong> to embed TypeScript commands inside a string. This only works where you use backticks <strong>`<\/strong> to surround the characters.<\/p>\n<p>If you run this and open a document, you will get a message about how many words there are in the document:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"486\" height=\"109\" class=\"wp-image-78039\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-49.png\" \/><\/p>\n<h2>Unit Testing<\/h2>\n<p>Included in the sample extension are some tests in: <em>src\/test\/extension.tests.ts<\/em>. If you take the code that counts the words in the document and make it testable by putting it into its own file which you could call from either the tests or the extension code, you could write some tests to verify that it works.<\/p>\n<p>The first thing to do is to create a new <em>wordCounter.ts<\/em> file under <em>src<\/em>. In this file, you will export the <strong>wordCounter<\/strong> function so that you can call it from other places. In TypeScript, if you do not export a function, it is like making it private to the file. Add this text to the new file:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">'use strict';\r\n  export function countWords(str : string) : number {\r\n      \/\/from https:\/\/stackoverflow.com\/questions\/18679576\/counting-words-in-string\r\n      return str.trim().split(\/\\s+\/).length;\r\n  }<\/pre>\n<p>If you wanted to, you could have created a TypeScript \u2018class\u2019 to do this work for you. In this case, a \u2018function\u2019 is more than enough. Save the new file, and, if you have a watcher task running or you run the build command in the <em>out<\/em> folder, you should see a <em>wordCount.js<\/em> and map file created automatically:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1013\" height=\"519\" class=\"wp-image-78040\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-50.png\" \/><\/p>\n<p>Open the <em>src\/tests\/extension.tests.ts<\/em> file and add a new <strong>suite<\/strong> at the bottom:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">suite(\"wordCounter Tests\", () =&gt; {\r\n              \r\n      });   <\/pre>\n<p>To use the <strong>wordCounter<\/strong> code, you must add an <strong>import <\/strong>to it under the other imports:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">import {countWords} from '..\/wordCounter';<\/pre>\n<p>In this case, you just want the single function, but you could import all the exported functions and classes from a file. You will note the <strong>..\/<\/strong>. This is used because the tests are being compiled in a subdirectory of the <em>src<\/em> directory, so the tests need to navigate up a level to get to the code. When you import the <strong>wordCounter<\/strong> function in the extension, because they are in the same folder, you won&#8217;t need the <strong>..<\/strong> part.<\/p>\n<p>Now that you have imported the code, you can add a test:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">test(\"3 words with funny separators\", () =&gt; {\r\n          assert.equal(3, countWords('a\\rnew\\tword'));\r\n      });<\/pre>\n<p>&nbsp;<\/p>\n<p>You can run the tests by opening the command palette and running <em>Tasks: Run Test Task<\/em> and choosing <em>npm:test<\/em>. Hopefully, you should see the test passing:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1014\" height=\"978\" class=\"wp-image-78041\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-22.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\16-wordCounter-tests.png\" \/><\/p>\n<p>You can now modify the actual code to use the new shared <strong>wordCounter<\/strong> function. Open <em>sec\/extension.ts<\/em> and add this <strong>import <\/strong>underneath the existing VS Code import:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">import {countWords} from '.\/wordCounter';<\/pre>\n<p>Delete the local function, and then the aliased function should already be available. If you have a build watcher, you will see if this has succeeded or if there were any errors:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-78042\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/c-users-ed-appdata-local-microsoft-windows-inetca-23.png\" alt=\"C:\\Users\\ed\\AppData\\Local\\Microsoft\\Windows\\INetCache\\Content.Word\\17-build-update.png\" width=\"1016\" height=\"1172\" \/><\/p>\n<h2>Publishing the Extension<\/h2>\n<p>&nbsp;<\/p>\n<p>Now that you have the extension, you will want to share it with others. You have two choices: you can create a VSIX (deployment package) and keep it private, or you can publish it to the Visual Studio Marketplace to share it with the world. Either way, you will need the vsce command line tool, so install that using:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">npm install -g vsce<\/pre>\n<p>vsce performs some basic checks. One of them is that the <em>README.md<\/em> file doesn&#8217;t contain the default text, so delete it all and write a nice <em>README<\/em> for your extension. When you are ready to run execute this from the same directory that holds <em>package.json<\/em>:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">vsce package<\/pre>\n<p>If this succeeds, you will get an <em>extension-version.vsix<\/em> file which can be loaded into VS Code using the command <strong>Extensions: Install from VSIX<\/strong>.<\/p>\n<p>To publish to the marketplace, you will need a personal access token or PAT which you can get from VSTS. To find a PAT that can be used to publish your extension, you need to go to any VSTS account that you log in using the same email that you used to create the publisher account. When you open VSTS, click on your name icon in the top right corner and choose security:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"476\" height=\"453\" class=\"wp-image-78043\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-51.png\" \/><\/p>\n<p>Then click on <em>Personal Access Tokens<\/em> and <em>Add<\/em>. You will see the <em>Create personal access token<\/em> screen. Give it a name, a length of time the token lasts for and most importantly, choose <em>All Accessible Accounts<\/em>, under the scopes you need, <em>Marketplace (publish)<\/em>:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"811\" class=\"wp-image-78044\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2018\/04\/word-image-52.png\" \/><\/p>\n<p>If you click <em>Create Token<\/em> at the bottom of the page, you will be given a token. Make a note of it as you will need to re-generate a new one if you lose it.<\/p>\n<p>When you run <strong>vsce publish<\/strong>, it will ask you for your <em>Personal Access Token<\/em>. Add the token you just saved, and the extension should be published and available to everyone in the market place.<\/p>\n<h2>Conclusion<\/h2>\n<p>VS Code is a refreshing development experience, and the ability to quickly write extensions is a real benefit. Many users are actively downloading extensions from the marketplace. If you write a useful extension, then it can potentially be used by a considerable amount of people easily and efficiently. TypeScript and JavaScript have been given quite a bad reputation, but they are reasonably straightforward languages with plenty of help and tutorials available online.<\/p>\n<h2>References<\/h2>\n<p><a href=\"https:\/\/code.visualstudio.com\/docs\/extensions\/example-hello-world\">https:\/\/code.visualstudio.com\/docs\/extensions\/example-hello-world<\/a><\/p>\n<p><a href=\"https:\/\/code.visualstudio.com\/docs\/extensions\/overview\">https:\/\/code.visualstudio.com\/docs\/extensions\/overview<\/a><\/p>\n<p><a href=\"https:\/\/www.typescriptlang.org\/\">https:\/\/www.typescriptlang.org\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>VS Code is a cross platform lightweight development environment from Microsoft. TypeScript is a superset of JavaScript. Ed Elliott shows you how to create a VS Code extension using TypeScript in this article.&hellip;<\/p>\n","protected":false},"author":59464,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[143538],"tags":[95509],"coauthors":[11314],"class_list":["post-78021","post","type-post","status-publish","format-standard","hentry","category-dotnet-development","tag-standardize"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/78021","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/59464"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=78021"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/78021\/revisions"}],"predecessor-version":[{"id":78055,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/78021\/revisions\/78055"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=78021"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=78021"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=78021"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=78021"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}