Writing Visual Studio Code Extensions
- Configuring a Visual Studio Code Extension from Jul 15, 2020, 7:51am
Similar to the work on creating a browser extension such as URL Ratâpart 1 and part 2 for what it took to make those workâIâve been looking at what it takes to develop an extension for Visual Studio Code, VSCode Rat.
Setup
The easy way to get moving is to use the Yeoman scaffolding tool to generate the base code for you.
$ 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 to the right)â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.
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.
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, 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:
"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:
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, conveniently licensed CC-BY-SA 4.0, like the rest of the blog.
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:
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, this post is getting long enough that itâs probably a better idea to save that configuration 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 and made available (like the application and their entire site) under the terms of the MIT License.
By commenting, you agree to follow the blog's Code of Conduct and that your comment is released under the same license as the rest of the blog. Or do you not like comments sections? Continue the conversation in the #entropy-arbitrage chatroom on Matrix…
Tags: techtips programming vscode