VSCode
VSCode is a ubiquitous IDE developed by Microsoft.
Extension Development
Getting started
Check out the Your First Extension docs page for more information.
Using their generator
If you are completely new to VSCode extension development, then this method is recommended.
Install yo and generator-code, as well as vsce for packaging later.
# This is using Bun, but you could just as easily use any other manager.
# NOTE! vsce could be a dev dependency here instead. You decide :)
bun install --global yo generator-code vsce
Then follow the on-screen prompts to complete the setup.
After this I personally recommend that you take stock of what it's set up, and if you need it or not. This scaffolding function is very opinionated, and will create ESLint and TypeScript configuration files for you.
From scratch
(If you previously globally installed those build tools, you can remove them now.)
# Again, you can use whatever you want here.
bun uninstall --global yo generator-code vsce
- Set up a directory.
mkdir <my-extension> ; cd <my-extension>
- Initialise this folder to be a from-scratch TypeScript project. You can do that a number of ways:
# Bun's defaults are surprisingly good for this.
bun init
# You could use npm, yarn, whatever.
npm init
# If using npm or yarn, install TypeScript and run their init.
npm install -D typescript
npx tsc --init
- Specify the
engines
,categories
,activationEvents
,main
, andcontributes
keys in your package.json:
{
...,
// This just tells NPM what this package runs on (VSCode).
"engines": {
"vscode": "^1.82.0"
},
// This is VSCode extension metadata.
"categories": [],
// This tells VSCode when to run your code.
"activationEvents": [],
// This tells VSCode what the entrypoint to your extension is.
"main": "./out/index.js",
// This tells VSCode what your extension does.
"contributes": {},
...
}
For more info on the engines
property, see the official docs.
- Install
@vscode/vsce
for packaging, and any other required library.
# You will always need this one.
bun add -D @vscode/vsce
# If you're using TypeScript, these ones are practically required.
bun add -D typescript @types/node @types/vscode
At this stage, unless you really know what you're doing - I'd also suggest installing a recommended tsconfig.json.
bun add -D @tsconfig/recommended
Replace your TSConfig with:
{
"extends": "@tsconfig/recommended/tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"types": ["bun-types", "vscode", "node"]
}
}
- Export two functions from your index.ts,
activate()
anddeactivate()
.
export function activate() {}
export function deactivate() {}
Create a LICENSE.md. See Software Licensing to see one to pick.
Set up your Git repository, filling out the
repository
field in your package.json.
{
"repository": {
"url": "https://really-cool.repository.com"
}
}
Running a test build
You now have all the source needed for a VSCode extension 🥳!
To build:
# Build to the final JavaScript bundle
bunx tsc
# Package into a VSCode extension
bunx vsce package
Ensure that the main
key in your package.json is the same as the outDir
/outFile
as specified in your tsconfig.json.
You can then install the extension to your local VSCode by running:
# Replacing <whatever> with your extension.
code --install-extension whatever-0.0.0.vsix
Wrapping up the start
From here, I'd recommend a couple of scripts added to package.json:
{
...,
"scripts": {
"compile": "tsc",
"package": "vsce package",
// vscode:prepublish is a special script as used by vsce.
"vscode:prepublish": "bun compile"
}
}
Adding a new command
For more info on extension commands, see their official documentation.
- Start by adding the command metadata to your package.json
contributes
field:
{
"contributes": {
"commands": [
{
// The internal ID of your command
"command": "myExtension.internalName",
// The display name of your command to users
"title": "Go crazy aaaa",
// Optional, also used in the display name "Stuff: Go crazy aaaa"
"category": "Stuff",
// Optional, 16x16 svg with 2px padding icon for display
"icon": {
"light": "path/to/light/icon.svg",
"dark": "path/to/dark/icon.svg"
}
}
]
}
}
- In your extension entrypoint, register and push the command into your extension context:
import { ExtensionContext, commands } from "vscode";
export function activate(context: ExtensionContext): void {
context.subscriptions.push(
commands.registerCommand("myExtension.internalName", () => {
console.log("Hi 👋");
})
);
}
export function deactivate() {}
Note
The name given in registerCommand()
MUST match the name in your contributes
.
Debugging and Troubleshooting
"Help! My extension won't activate and I don't know why!"
Failures at activation can be spotted in the Extension Host logs.
You can get to the Extension Host logs either from the Output tab, or if there is no output tab:
- ⌘ + ⇧ + P to open the command palette (or the Windows equivalent)
- Run the command Output: Show output channels
- Type Extension Host and hit enter.
Configuration
For more configuration options than are listed here, check out the extension manifest.
Activation
Your extension will start doing things in the editor when it is both 'loaded' and 'activated'.
This is because VSCode will lazyload extensions to save on resources - you don't need the Python extension loaded if you aren't editing Python files.
The most common activation type is onLanguage
, which will activate when a file matching one of VSCode's language identifiers gets opened.
{
...,
"activationEvents": [
// For my really cool Java NetBeans extension.
// It'll take off, I swear 😎.
"onLanguage:java"
],
...,
}
To break out of this system entirely (and be a bit of a jerk) VSCode provides a *
activation event, which will just always run.
{
// I am a real jerk. Take that, other extensions 😈.
"activationEvents": ["*"]
}
For more information, check out the VSCode documentation on activation events.
Contributes
VSCode can keep track of things that your extension does without it being activated, these are registered in the extension using contribution points.
For more information, check out the VSCode documentation on contribution points.