Writing Visual Studio Code Extensions
Tags: techtips, programming, vscode
Published: 2020-07-08 13:58:34

---
title: Writing Visual Studio Code Extensions
published: true
date: 2020-07-08 11:15:02 UTC
tags: techtips,programming,vscode
canonical_url: https://john.colagioia.net/blog/2020/07/08/vscode.html
---

As a quick note, I [released this post](https://john.colagioia.net/blog/2020/07/08/vscode.html) on my blog, this morning, so it can get to be (as I tend to be) a bit rambling.  Oh, and the original text is [on GitHub](https://github.com/jcolag/entropy-arbitrage) (licensed CC-BY-SA), so if anything seems muddy, by all means:

 * Leave a comment here,
 * Leave a comment on the blog,
 * File an issue on GitHub, or
 * Add a pull request!

{% github jcolag/entropy-arbitrage no-readme %} 

* * *

![VSCodium](https://dev-to-uploads.s3.amazonaws.com/i/2ctffo2kckwpycwwo133.png "VSCodium")

Similar to the work on creating a browser extension such as [**URL Rat**](https://github.com/jcolag/url-rat)—[part 1](/blog/2020/06/17/plugin.html) and [part 2](/blog/2020/06/24/store.html) for what it took to make those work...

{% github jcolag/url-rat no-readme %} 

{% link jcolag/writing-browser-extensions-4gkh %}

{% link jcolag/writing-browser-extensions-with-configuration-4gm4 %}

I’ve been looking at what it takes to develop an extension for Visual Studio Code, [**VSCode Rat**](https://github.com/jcolag/vscode-rat).

{% github jcolag/vscode-rat no-readme %} 

## Setup

The easy way to get moving is to use the [Yeoman](https://yeoman.io/) scaffolding tool to generate the base code for you.

```console
$ npm install --global yo generator-code
$ yo code
```

The yeoman (that’s the ASCII art fellow) will then ask for some basic information:

- The kind of extension: A normal extension, color theme, language, snippet, key map, or pack of extensions;
- The extension’s name;
- The extension’s ID;
- The extension’s description;
- Whether you want type-checking in the configuration file;
- Whether you need a git repository; and
- Your preferred package manager.

This list of questions might change by the time you read this, but it should give you an idea of what to expect, regardless.

The file that matters most, of course, is `extension.js`. There’s also `jsconfig.json`, which you can look at and—at least for now—confirm that it’s mostly just the answers you gave to the questions.

### Packaging

If you’re not familiar with Node.js projects, you’ll also want to get familiar with `package.json`. You rarely need to update it manually, but it’s good to know what’s lurking in there. Whether you know this file or not, the new aspects are:

- `activationEvents`: The term is not subtle, referring to the list of events that activate the extension’s code. Visual Studio Code won’t load the extension until one of those events occurs. The default is a custom “Hello, World” event.
- `commands`: Here, you’ll find a list of commands the user can run to operate the extension. The default—probably no surprise, given the custom event’s name—is a “Hello, World!” command.

You can probably guess that those are going to be important.

## Try It

The easy way to test the extension is to open the folder in Visual Studio Code for editing and use the existing scripts to launch a new instance of Code with your extension loaded. To do that, click the “Run” button on the left (#1 on the picture below)—you can also type Ctrl+Shift+D—to get to the run/debug commands, then click the green _Start Debugging_ arrow, marked #2 in the image.

![Run VSCode with the extension](https://dev-to-uploads.s3.amazonaws.com/i/vs4ah1glrp7n4rtf0a6a.png "Run VSCode with the extension")

This will start a second Visual Studio Code window. From here, you can open the Command Palette (_View_/_Command Palette…_ or Ctrl+Shift+P) and, as you start typing `Hello`, you’ll see the new “Hello World” command listed. Select it and you’ll get a little toast notification, fulfilling the extension’s registered action.

![Hello, back](https://dev-to-uploads.s3.amazonaws.com/i/2rla410892b64x8eh2lm.png "Hello, back")

So, that’s what we get without doing *any* work.

## Monitoring Files

So, now we need to fill in real code.

The first step is going to be to give our extension a reason to run _other_ than a manual command. Microsoft has provided a [list of activation events](https://code.visualstudio.com/api/references/activation-events), but as mentioned, that tells Visual Studio Code when to _load_ the extension, not when to _run_ the extension.

In our case, since we want to monitor what our user is doing—just like with **URL Rat**, _please_ don’t try to use this in production, because it’s a security nightmare—we want to load the extension when the editor starts, so that just looks like this:

```json
"activationEvents": [
  "*"
],
```

As in, change the `activationEvents` list in `package.json` to that.

Then, take a look at `extension.js`. The only code that looks important is `activate()`, which registers and subscribes to the command. So, we need to make our changes, there, subscribing to events that are relevant. Try something like the following in that `activate()` function:

```javascript
vscode.workspace.onDidSaveTextDocument((document) => {
  const file = document.fileName;
  vscode.window.showInformationMessage(`Saved ${file}`);
});

vscode.workspace.onDidOpenTextDocument((document) => {
  const file = document.fileName;
  vscode.window.showInformationMessage(`Opened ${file}`);
});

vscode.workspace.onDidChangeTextDocument((changes) => {
  const file = changes.document.fileName;
  vscode.window.showInformationMessage(`Modified ${file}`);
});
```

This is extremely primitive, but it points us in the right direction. Rather than pop up the static message when the user asks for it, we now pop up messages when files are opened, saved, and modified.

## Sending to a Server

Unfortunately, the version of JavaScript supported by Visual Studio Code doesn’t _appear_ to support `fetch()`, so we can’t pull off the same trick we did with **URL Rat**. Instead, we need a function that looks something like [this one](https://stackoverflow.com/a/50891354/3438854), conveniently licensed CC-BY-SA 4.0, like the rest of my blog.

{% stackoverflow 50891354 %}

```javascript
function httpPost({body, ...options}) {
  return new Promise((resolve,reject) => {
      const req = http.request({
          method: 'POST',
          ...options,
      }, res => {
          const chunks = [];
          res.on('data', data => chunks.push(data))
          res.on('end', () => {
              let body = Buffer.concat(chunks);
              switch(res.headers['content-type']) {
                  case 'application/json':
                      body = JSON.parse(body.toString());
                      break;
              }
              resolve(body)
          })
      })
      req.on('error',reject);
      if(body) {
          req.write(body);
      }
      req.end();
  })
}
```

Then, we can replace our `vscode.window.showInformationMessage()` function calls with something like the following:

```javascript
httpPost({
  body: `Opened ${document.fileName}`,
  headers: {
    'Content-Type': 'text/plain',
  },
  hostname: 'localhost',
  path: '/'
});
```

The verb used in the string depends on which action we’re talking about, of course. In addition, we might want to do something more with the change actions. When the files change, the object contains the same filename information as the other actions, but also includes enough information to describe the change, including the replacement text, the file offset where the change occurs, and the start and end line and column of the change.

With that information, we can collect the incremental changes made in Visual Studio Code over time, which would make it easy to reconstruct ideas that were either deleted and forgotten or lost in a crash.

## What’s Left?

Like with the “first draft” of **URL Rat** , you probably noticed that **VSCode Rat** uses a hard-coded URL, whereas we would obviously want to be able to configure that information, just like we would with any other extension. And just like with the browser extension posts, this post is getting long enough that it’s probably a better idea to save that configuration discussion for next week.

Otherwise, it seems surprisingly difficult to know what events we can subscribe to and what they do (it took a long time to track down `vscode.workspace.onDidSaveTextDocument()`, probably longer than it should have), but once you know them, the rest of the development is remarkably fast.

* * *

**Credits** : The header image is the sample image for [VSCodium](https://vscodium.com/) and made available (like the application and their entire site) under the terms of the MIT License.