Scotch.io's Real-World Vue Book is launching! Get 50% Off
We're live-coding on YouTube! Join us!

When Visual Studio Code doesn't do what you want it to, you install an extension. When you can't find an extension that does exactly what you need, you create your own! In this article, we will look at creating your first Visual Studio Code extension.

TLDR - Install the tools, generate a new extension, add your desired functionality!

Check out Learn Visual Studio Code to learn everything you need to know about about the hottest editor in Web Development for just $10!

Installing the Tools

The Visual Studio Code team created a generator for creating extensions. This generator will generate all of the necessary starter files to begin creating your extension, which makes the process much easier.

Visual Studio Code team created a generator to make it easy to get started writing extensions!

To get started, you will need to have Yeoman installed, which is a scaffolding tool. According to their docs (screen below), you can install Yeoman (assuming you have Node.js installed), by running

npm install -g yo

With Yeoman installed, now you need to install the specific generator for Visual Studio Code extensions.

npm install -g yo generator-code

Create Your First Extension

You are now ready to create your first extension! To do so, run the following command.

yo code

You will then answer several questions about your project. You will need to choose what kind of extension you are creating and between TypeScript and JavaScript. We will be choosing JavaScript for this one.

Then you've got a few more questions.

  • name
  • identifier
  • description
  • type checking (yes)
  • do you want to initialize a git repository (yes)

After this process is complete, you've got all of the files you need to get started! Your two most important files are...

  • package.json
  • extension.js

Open the package.json and let's take a look. There's the typical stuff that you're used to like name, description, etc. But there are 2 more sections that are very important.

  • activationEvents - this is a list of events that will activate your extension. Extensions are lazy loaded so they aren't activated until one of these activation events occur
  • commands - list of commands that you provide the user to run via your extension

We will come back to these shortly!

You can also take a look at the extension.js file. This is where we are going to write the code for our extension. There's some boilerplate code in here, so let's break it down.

In the highlighted line below is where our command is being registered with VS Code. Notice that this name "extension.helloworld" is the same as command in the package.json. This is intentional. The package.json defines what commands are available to the user, but the extension.js file actually registers the code for that command.

In this Hello World example, all this command will do is display a Hello World message to the user.

Debugging Your Extension

Now that we have all of the necessary files installed, we can actually run our extension. Again, VS Code does a great job of making this incredible easy.

Code Generator creates debug configurations for you, so you can start developing/debugging immediately!

Did you notice the ".vscode" folder? This is where VS Code stores configuration files of sorts for your project. In this case it includes a launch.json which contains debug configurations.

From here, we can debug. Open the debug tab on the left (yes, it looks like a bug), and then press play.

This will open up a new (debug) instance of VS Code.

With this debug instance of VS Code open, you can open the command palette with Cmd+Shift+P on Mac or Control+Shift+P on Windows and run "Hello world".

You should see a hello world message pop up in the lower right hand corner!

Editing Your Extension

Before we work on code, let's take one more look at the activationEvents section in the package.json file. Again, this section contains a list of events will activate our extension whenever they occur. By default, it is set to activate when our command is run.

In theory, this event could be anything, and more specifically "*" anything. By setting the activation event to "*" this means your extension will basically be loaded when VS Code starts up. This is not required by any means, just a note.

Ok, so we've got the necessary files and we know how to debug. Now let's start building our extension. Let's say we want this extension to be able to create an html file that already has boilerplate code in it (otherwise we would have to create the file ourselves and then write the code) into our project.

Let's first update the name of our command. In the extension.js, update the name of the command from "extension.helloworld" to "extension.createBoilerplate".

Now, update the package.json accordingly.

Writing Visual Studio Code extensions is just writing Node!

Now, let's write our functionality. The first thing we'll do is require a couple of packages. We are going to use the fs (file system) and path modules.

const fs = require("fs");
const path = require("path");

We also need to get the path to the current folder. It took me a bit of digging (and many console logs) to figure out but I eventually did. Inside of the command, add the following snippet.

      const folderPath = vscode.workspace.workspaceFolders[0].uri
        .toString()
        .split(":")[1];

We will also need to store our boilerplate html code into a variable so that we can write that to a file. Here's the boilerplate html.

 const htmlContent = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <link rel="stylesheet" href="app.css" />
</head>
<body>
    <script src="app.js"></script>
</body>
</html>`;

Now we need to write to the file. We can call the writeFile function of the file system module and pass in the folder path and html content.

Notice that we use the path module to combine the folder path with the name of the file we want to create. Then inside of callback, if there is an error, we display that to the user. Otherwise, we let the user know that we created the boilerplate file successfully!

fs.writeFile(path.join(folderPath, "index.html"), htmlContent, err => {
        if (err) {
          return vscode.window.showErrorMessage(
            "Failed to create boilerplate file!"
          );
        }
        vscode.window.showInformationMessage("Created boilerplate files");
      });

Here's what the full function looks like.

const folderPath = vscode.workspace.workspaceFolders[0].uri
        .toString()
        .split(":")[1];

      const htmlContent = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Document</title>
    <link rel="stylesheet" href="app.css" />
</head>
<body>
    <script src="app.js"></script>
</body>
</html>`;

      fs.writeFile(path.join(folderPath, "index.html"), htmlContent, err => {
        if (err) {
          return vscode.window.showErrorMessage(
            "Failed to create boilerplate file!"
          );
        }
        vscode.window.showInformationMessage("Created boilerplate files");
      });
    }

Go ahead and debug your newly developed extension. Then, open up the command palette and run "Create Boilerplate" (remember we changed the name).

After running the command, you should see the newly generate index.html file and a message to let the user know!

Recap

If there's something VS Code doesn't do, install an extension. If you can't find the right extension, create it yourself. The Code team has done a great job of creating tools to help make extension development pretty streamlined.

The hardest part in my mind is reading through the documentation to learn what APIs there are to use and how to use them. So, I would recommend spending some time in the docs!

Like this article? Follow @jamesqquick on Twitter