Are you about to start developing a VSCode extension? Or maybe you struggle with what you’re currently trying to achieve?
At Packmind, we’ve built a VSCode extension to help developers share their best coding practices, access them in their IDE, and get automatic suggestions.
As we took some time to explore the possibilities offered by the VSCode API, we’re keen on sharing some useful material we found and we’d be happy if it can help you. And btw, feel free to ask in comments any more questions! 🙂
ToC of the questions answered in this post
- #1 How to access the visible lines in the current editor?
- #2 How to get the selected text?
- #3 How to add a marker to a line with a custom image?
- #4 How to get the programming language of the current editor?
- #5 How to add a menu on the right-click event?
- #6 How to set a custom shortcut to perform an action?
- #7 How to list opened VSCode extensions pane?
- #8 How to retrieve all the VSCode extensions installed?
- #9 How to underline a line of code?
- #10 How to retrieve the file name of the current tab?
- #11 How to listen when a tab has been closed?
- #12 How to listen when a tab has been opened?
- #13 How to listen when the current tab has changed?
- #14 How to add and access a VSCode extension setting?
- #15 How to implement a Caret Listener?
- #16 How to get the current line of the caret?
- #17 How to listen when the current editor is saved?
- #18 How to listen when a specific shortcut is called?
- #19 How to change the logo of my VSCode extension?
- #20 How can I request an input for a command?
- #21 How to prompt a warning notification?
ℹ️ Note that for all the following examples, you can use the following import
import * as vscode from 'vscode';
as the top of your file.
#1 How to access the visible lines in the current editor?
If your extension needs to only process source done visible on the user screen, this snippet is for you:
const editor = vscode.window.activeTextEditor;
if (editor) {
const firstVisibleLine = editor.visibleRanges[0].start.line;
const lastVisibleLine = editor.visibleRanges[0].end.line;
// do something with the visible lines
}
Note that the visibleRanges
property may return multiple ranges if the editor is split, whether horizontally or vertically. So you might need to process all the ranges in the array to access to the complete set of visible lines.
#2 How to get the selected text?
You can achieve this as follows:
const editor = vscode.window.activeTextEditor;
if (editor) {
const selection = editor.selection;
const text = editor.document.getText(selection);
console.log(`The selected text is: ${text}`);
}
#3 How to add a marker to a line with a custom image?
Here is how to add a marker with a custom image to the editor at line 10:
const editor = vscode.window.activeTextEditor;
if (editor) {
const lineNumber = 10; //Your line
const start = new vscode.Position(lineNumber, 0);
const end = new vscode.Position(lineNumber, 0);
const decorationRange = new vscode.Range(start, end);
const decorationOptions = {
gutterIconPath: '/path/to/your/custom/image.png',
gutterIconSize: 'contain', //Available values are 'auto', 'contain', 'cover' and any percentage value. For further information:
};
const decoration = vscode.window.createTextEditorDecorationType(decorationOptions);
editor.setDecorations(decoration, [decorationRange]);
}
#4 How to get the programming language of the current editor?
Even though you could use the filename extension, the VSCode editor stores a languageId property:
const editor = vscode.window.activeTextEditor;
if (editor) {
let language = editor.document.languageId;
console.log(`The current document's language is ${language}`);
}
You can find the whole list of the language identifiers on this documentation.
#5 How to add a menu on the right-click event?
Here is an example of how to set a command and register an item in the menu.
export function activate(context: vscode.ExtensionContext) {
// define the command and action
const commandId = 'extension.promyzeSayHello';
const sayHello = () => {
vscode.window.showInformationMessage('Hello World! This is Promyze!');
};
// create the command and menu item
const command = vscode.commands.registerCommand(commandId, sayHello);
const menuItem = vscode.commands.createMenuItem({
command,
label: 'Get Hello from Promyze',
});
// add the menu item to the context menu
vscode.window.contextMenus.append(menuItem);
// dispose the command and menu item when deactivated
context.subscriptions.push(command, menuItem);
}
#6 How to set a custom shortcut to perform an action?
A custom key binding can help your users in performing some commands available in your extension:
export function activate(context: vscode.ExtensionContext) {
// define the command and action
const commandId = 'extension.promyzeSayHello';
const sayHello = () => {
vscode.window.showInformationMessage('Hello World! This is Promyze!');
};
// register the command and bind the key
context.subscriptions.push(
vscode.commands.registerCommand(commandId, sayHello),
vscode.keybindings.bindKey('ctrl+shift+p', sayHello),
);
}
Note that you must register the command and bind the key in separate calls to these methods. Don’t forget to add these calls to the subscriptions
array of the ExtensionContext
object, so that they can be disposed when the extension is deactivated.
To use a custom keybinding, you need to define it in the keybindings.json
file of your extension:
{
"key": "ctrl+shift+p",
"command": "extension.promyzeSayHello",
"when": "editorTextFocus"
}
In this example, the key binding should run the command only when the text editor has the focus.
#7 How to list opened VSCode extensions pane?
To retrieve the list of opened extensions panes, you can do it as follows:
const visiblePanels = vscode.window.visibleTextEditors.filter(editor => editor.viewColumn);
console.log(`There are ${visiblePanels.length} opened extensions panes.`);
visiblePanels.forEach(panel => {
console.log(`- Pane: ${panel.title}`);
});
This code filters the array of visible text editors to only include those with a view column (i.e., extensions panes).
#8 How to retrieve all the VSCode extensions installed?
Depending on your use cases, you can be interested in listing the others extensions installed in VSCode:
export function activate(context: vscode.ExtensionContext) {
const extensions = context.extensions;
console.log(`There are ${extensions.length} extensions installed.`);
extensions.forEach(extension => {
console.log(`- Extension: ${extension.id} (${extension.extensionPath})`);
});
}
#9 How to underline a line of code?
If you plan to highlight a piece of code to notify your users, for instance if your extension acts as a linter, here’s how you can add make it (here with a blue line on the whole line):
const editor = vscode.window.activeTextEditor;
if (editor) {
const lineNumber = 0;
const start = new vscode.Position(lineNumber, 0);
const end = new vscode.Position(lineNumber, editor.document.lineAt(lineNumber).text.length);
const decorationRange = new vscode.Range(start, end);
const decorationOptions = {
isWholeLine: true,
overviewRulerLane: vscode.OverviewRulerLane.Right,
overviewRulerColor: 'blue',
textEditorDecorationType: vscode.TextEditorDecorationType.Underline
};
const decoration = vscode.window.createTextEditorDecorationType(decorationOptions);
editor.setDecorations(decoration, [decorationRange]);
}
#10 How to retrieve the file name of the current tab?
The code below shows how to get the filename but also the full path if you need it:
const editor = vscode.window.activeTextEditor;
if (editor) {
const fileName = editor.document.fileName;
console.log(`The file name of the current tab is ${fileName}.`);
//To get the full path
const fileUri = editor.document.uri;
const filePath = fileUri.fsPath;
}
#11 How to listen when a tab has been closed?
This can make sense if you maintain a cache for instance for every opened tab, and thus you’ll avoid memory leaks.:
export function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidCloseTextDocument(document => {
console.log(`Tab closed: ${document.fileName}`);
});
}
#12 How to listen when a tab has been opened?
To run a piece of code every time a new tab is opened, this code is for you:
export function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidOpenTextDocument(document => {
console.log(`New tab opened: ${document.fileName}`);
});
}
It is worth noting that the event will not be triggered if a document is opened and is not in the project or workspace..
#13 How to listen when the current tab has changed?
This will help if you need to trigger a piece of code when the active editor has been updated:
export function activate(context: vscode.ExtensionContext) {
vscode.window.onDidChangeActiveTextEditor(editor => {
if (editor) {
console.log(`Current tab changed to: ${editor.document.fileName}`);
}
});
}
You can also use the onDidChangeVisibleTextEditors
event, triggered when the set of visible text editors changes.
#14 How to add and access a VSCode extension setting?
To use the Promyze VSCode extension, our users need to input an API Key in the extension settings.
This field should be defined in the package.json
file of your extension, in the "configuration"
section, to the "properties"
field:
{
"contributes": {
"configuration": {
"type": "object",
"title": "Promyze Settings",
"properties": {
"promyzeApiKey": {
"type": "string",
"default": "",
"description": "The Promyze API Key"
}
}
}
}
}
To read the setting value in your code, you can use the workspace.getConfiguration
method:
const promyzeApiKey = vscode.workspace.getConfiguration().get('promyzeApiKey');
#15 How to implement a Caret Listener?
You can use the onDidChangeCursorPosition
event of the TextEditor
object, triggered whenever the cursor position changes within the editor:
const editor = vscode.window.activeTextEditor;
if (editor) {
editor.onDidChangeCursorPosition(e => {
console.log(`Cursor moved to line: ${e.position.line}, column: ${e.position.character}`);
});
}
You can also use onDidChangeCursorSelection
event triggered when the cursor selection changes.
#16 How to get the current line of the caret?
Getting the current line of the caret (cursor) can make sense if you want compute operations based on the current context:
const editor = vscode.window.activeTextEditor;
if (editor) {
const position = editor.selection.active;
const line = position.line;
console.log(`Current caret line: ${line}`);
}
The Position
object represents the current position of the cursor. You could also get the same result by using the editor.document.lineAt(position)
method, like this:
const currentLine = editor.document.lineAt(position);
console.log(`Current caret line: ${currentLine.text}`);
#17 How to listen when the current editor is saved?
This is useful if you want to run actions when the user has saved the content of the opened editor.
You need to use the onDidSaveTextDocument
event of the workspace
module, triggered whenever a text document is saved:
export function activate(context: vscode.ExtensionContext) {
vscode.workspace.onDidSaveTextDocument(document => {
if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document === document) {
console.log(`The editor has saved the following file: ${document.fileName}`);
}
});
}
Note that in this example it checks if the document saved is the one currently under edition.
#18 How to listen when a specific shortcut is called?
Do you need to perform an action after a user has copy/cut a piece of code? You can then use the commands
module and register a command for the shortcut.
Here is an example when the editor.action.clipboardCutAction
command is called (a line is cut):
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand('editor.action.clipboardCutAction', () => {
console.log('The cut command has been called');
}));
}
You can find all the list of keybindings here.
#19 How to change the logo of my VSCode extension?
You need to specify the path to your image in the "icon"
property of your package.json
file. The path can be relative or absolute:
"icon": "yourIcon.png"
PNG, JPEG and GIF formats are supported by VSCode. And don’t forget to include the file in your extension ;)
#20 How can I request an input for a command?
The vscode.window.showInputBox()
function will help you to prompt a user input, for instance when executing a command:
export function createBestPracticeCommand() {
// perform some action
vscode.window.showInputBox({
placeHolder: 'Enter your best practice name'
}).then(value => {
// use the value entered by the user to retrieve the best practice name
});
}
After the user enters some input and presses 'Enter', the then()
callback will be invoked with the input as its parameter. Note that you can use async/await
for this method.
#21 How to prompt a warning notification?
Here's an example of how to make it:
vscode.window.showWarningMessage("This is a warning message", {modal: true})
.then(response => {
if(response === "ok"){
// user clicked OK
}
});
Here*,* {modal: true}
is an optional configuration object, that indicates that the message box should be modal. The second parameter can also be an array of options:
vscode.window.showWarningMessage("This is a warning message", 'ok', 'cancel').then(response => {
if(response === "ok"){
// user clicked OK
}
});
The warning message will be displayed along with two buttons 'ok' and 'cancel'.
Bonus:
- For info notifications, you can use showInformationMessage
- For error notification, you can use showErrorMessage
That’s all, folks! We hope it can help you start working on your VSCode extension.
To go further, the best place remains the official documentation.
Top comments (4)
Thanks a lot for your list! The official VS Code API docs are sometimes very sparse and it can be hard to figure out the underlying concepts and how some of the different components fit together. Having a guide that focuses more on typical use cases instead of just listing the types and functions is a great starting point.
If you plan to extend the list at some point, it might be a good idea to add some tips regarding webviews (How to create webviews? How to manage webview resources? How to save and restore the webview state? etc.). It took me quite a bit of time to figure out all the details of webviews when I started working on my extension SemanticDiff, and I can imagine that other developers might face similar issues as well.
Thanks Michael,
I won't hesitate to let you know if I'll work on it in the future ;)
Very cool, thanks 👍
You're welcome !