Trivia! Visual Studio Code does not share any lineage with the Visual Studio IDE. Microsoft used the VS brand for their enterprise audience which has led to a lot of confusion. The application is just Code in the command line and does not work at all like Visual Studio. It takes more inspiration from TextMate and Sublime Text than Visual Studio. It shares the snippet format of TextMate (Mac only) and forgoes the XML based format used in Visual Studio.
Why you should create an extension
On the surface, VS Code does not seem to provide many reasons for why anyone would create extensions for it. The platform has most of the same features that other editors have. The snippet format is powerful, and with extensions like Settings Sync, it is easy to share them over a Gist. Code (pun intended) is open source and the team is fairly responsive to requests. Basic support can be provided without a plugin by creating a “typings” file in the npm module.
Still, creating extensions for VS Code is something all web developers should try for the following reasons:
Learning: The web gets a lot of slack for its performance. VS Code demonstrates how you can develop a performance-sensitive applications in Electron and some of the techniques — the multi-process and cluster-oriented architecture is so good that all Electron apps should just steal it for their own use.
Fun: Most important, it is a lot of fun developing VS Code extensions. You can scratch an itch and end up giving back to the community and saving time for so many developers.
Trivia! Even though TypeScript is an independent programming language with many popular uses including the Angular (2+) framework, VS Code is the sister project with the most impact on the language. Being developed in TypeScript as a TypeScript editor, VS Code has a strong symbiotic relationship with TypeScript. The best way to learn the language is by looking at the source code of Visual Studio Code.
What an extension can do
VS Code exposes multiple areas where an extension can create an impact. The exposed touch points could easily fill a book. But there’s unlikely a situation where you need all of them, so I’ve put together a listing here.
These outline the various places that can be extended as of version 1.25:
Variables, expressions, reserved words, etc. for highlighting
Items in autocomplete with tab-based navigation to replace certain items
Auto-close and indent items like quotes, brackets, etc.
Hover action item
Documentation tooltip on hover
Autocomplete items when typing
Squiggly red underlines to indicate potential errors
Method signature tooltip while typing
Location of the code where the symbol is defined in the inline editor and go to the definition (including a ⌘+Hover tooltip)
For the inline editor with links, all files, and places associated with the symbol
List of all positions where the selected symbol exists for highlighting
List of symbols navigable from the Command menu with an @ modifier
Symbol provider for an entire workspace
Squiggly green underline to indicate fixable errors with a fix action on click
Always present inline metadata with clickable action items
Support for renaming symbols used in multiple places
Fix indentation and formatting for the entire document
Document Range Formatting
Fix indentation and formatting for selected text
On Type Formatting
Fix formatting and indentation in real time
Color popup menu alternative
Override settings and save as a new set of defaults
Commands in the ⌘+P menu
Menu items in the top level menu, any menus alongside the document tab bar, and context menus
Debugger settings for debugging a new or existing language
Overrides to support a custom source control system
Same as code snippets above
A new section in one of the docked panels on the left
A new docked panel on the left bar
TypeScript server plugins
Overrides for the built-in TypeScript language server
Alternative page in parallel to a document to show a custom rendering of a document or any custom HTML (unlike a View Container that is docked to a side)
Decoration on the gutter of the text area
Popups for error, warning and informational messages on the bottom-right
Multi-select option selector menu
Text box for the user to input values
Status Bar Item
Icon, button, or text in the status bar
Show a progress indicator in the UI
Create a tree like the one used to define the workspace (which can be put inside a View or a View Container)
Custom code folding into the plus button on the gutter
The implementation provider (languages like and TypeScript can have declaration and implementation as separate)
Diff view in source control mode
For commits in source control mode
Apart from this, you can invoke any functionality that the user can invoke in any of the menus with whatever parameters the user can pass. There are events on almost every functionality as well as a lot of file systems and text file-related utility methods.
Let’s start building
Alright, enough with the preamble — let’s start putting an extension together with what we’ve just learned.
Next up, configure the options. Here’s how I set things up:
TypeScript is optional, but highly recommended. Just another way to remind you that VS Code works really well with TypeScript.
Now, you can open the folder in VS Code. I would recommend checking the initial commit. You can look at the quick start guide that gets generated in the project to understand the file structure. Take a quick stab at seeing it live. Go to the debug panel and launch the extension in debug mode. Place a breakpoint inside the activate method in extension.ts to walk through this in action. The “Hello world” application already registers a command that can launch an informational message in the footer. Go to the commands menu with ⌘+⇧+P (Ctrl+Shift+P on Windows) and select hello world.
You can put another breakpoint in the registerCommand callback to get the command event.
Open package.json for a description of the plugin’s configuration. Change activationEvents to a more specific one based on what type of language or file protocol you want to support. All Autocomplete supports all file formats, indicated by an *. You should also look at the contributes section if you want to contribute to things like settings, commands, menu items, snippets, etc.
Many of these contributions have additional JSON in the same package.json file with all the information. Some APIs require code that has to be used in the activate call like vscode.commands.registerCommand for creating a command. For an autocomplete extension, the contributes section is not required and can be removed.
To use the same All Autocomplete options in extension.ts, replace the activate function with the following:
You can specify further details about the completion item, like attached documentation using more options in the object. Now, if you debug this and type H you should see Hello in the completion menu. The code to register most of the language-based providers is nearly the same.
You can see the All Autocomplete menu in vscode.languages, which provides options to register providers. Each provider has its own set of parameters that we can fill up similar to the completion item provider.
The document object provides access to the document with utility methods to access text at specific positions and ranges. It is strongly encouraged to use the APIs to access all documents instead of the raw Node.js APIs.
You can parse the document on demand or keep a data structure (like the trie used in All Autocomplete) optimized to search for inputs as the user is typing.
Tip: If you are looking for some text selection/manipulation APIs, there will most likely be an API already available. No need to reinvent the wheel. You can precisely get text with document.getText(document.getWordRangeAtPosition(position)). Alt+Click on any VS Code object to get to the class structure and JSDoc documentation.
Publishing the extension
Once the extension is complete, it is time to publish it to the marketplace. VS Code has a command line tool (vsce) for publishing but it does require creating an account.
Here’s how to prep the extension for submission:
Clean up the package: The package.json and README.md files provide the description and details about your extension that get displayed in the marketplace. It is essential to spruce up those files and fill all missing information so that the documentation comes out clean. Good to add some badges and a self-describing GIF to the repo.
Create an account: You need to create a Visual Studio Team Services (VSTS) account. This is the only place where VS Code ties up with Visual Studio. You need to sign up and get an access token. The VSTS interface is slightly confusing, but you don’t need to learn a new code management tool to publish. Go to the security section to get the access token. (Don’t make the same mistake as me and confuse the gear icon in the menu with security.)
Install: Use the vsce command line tool to publish extensions. It is available in npm and is extremely easy to use.
Tip: The access token for VSTS expires every year and therefore the account information is extremely important. It is also required to reply to the comments on the marketplace, though most users are active on GitHub and that is where you are more likely to get bugs and feature requests.
npm install -g vsce # One time installation vsce create-publisher <name> # One time create publisher vsce login # One time login. Asks for access token. vsce publish <version> # Publish or update the extension
VS Code does not compile extensions on the server. Make sure the output folder created by compiling your extension is up to date. Also be sure to check case sensitivity of your file names because incorrect file paths will break in Linux. Native modules in Node are a huge pain and should not be used. It is impossible to compile and provide all platform variants for specific Electron versions. (Someone needs to create a PhoneGap build for npm!) This will get better over time with WebAssembly and N-API.
Support and Maintenance
The VS Code team is super active on GitHub and StackOverflow. GitHub is the right place to file bugs that you might discover in the API. The team is fairly responsive though you need to make the context extremely clear as you would with any helpful bug report.
You should have a GitHub repository for your extension and expect users to file issues directly on GitHub. Expect VS Code users to be proficient with tools and technology (some of them may have a lot more knowledge that you). Even though it’s a free endeavor, keeping humility and treating issue reporters as customers is the right behavior.
Tips on performance
VS Code has good performance because it is built with an architecture that isolates things like extensions that can cause slowness. If your extension does not return in time, your output might be ignored.
A few things that can help maintaining the performance of the editor include:
Using the official APIs: It is easy to ignore them and build your own. The “typings” file is wonderful and has documentation for all of the available APIs. A five minute search there can save a lot of time. If you need some files, it is better in most cases to request VS Code to open it in an editor than it is to read it from a disk (unless you are reading thousands of files and not leaving them open).
Expose options: Ensure there is a way for users to turn off capabilities that rely on heavy events, like every keystroke. It may not seem noticeable on your machines, but this is not the place for that. Developers maintain their dot files forever and they spend time going through options if there is an issue they to work around. There is no harm in exposing a way to gracefully degrade in case every keystroke is not possible.
My extension is live on the VS Code marketplace and I would love to get your feedback on that as well. 🙂