DEV Community

Cover image for 21 questions you’ll ask if you code a VSCode extension
Cédric Teyton for Packmind

Posted on • Edited on • Originally published at packmind.com

21 questions you’ll ask if you code a VSCode extension

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';
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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}`);
}
Enter fullscreen mode Exit fullscreen mode

#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]);
}
Enter fullscreen mode Exit fullscreen mode

#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}`);
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

#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),
   );
}
Enter fullscreen mode Exit fullscreen mode

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" 
}
Enter fullscreen mode Exit fullscreen mode

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}`);
});
Enter fullscreen mode Exit fullscreen mode

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})`);
   });
}
Enter fullscreen mode Exit fullscreen mode

#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]);
}
Enter fullscreen mode Exit fullscreen mode

#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;
}
Enter fullscreen mode Exit fullscreen mode

#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}`);
   });
}
Enter fullscreen mode Exit fullscreen mode

#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}`);
   });
}
Enter fullscreen mode Exit fullscreen mode

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}`);
      }
   });
}
Enter fullscreen mode Exit fullscreen mode

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"
            }
         }
      }
   }
}
Enter fullscreen mode Exit fullscreen mode

To read the setting value in your code, you can use the workspace.getConfiguration method:

const promyzeApiKey = vscode.workspace.getConfiguration().get('promyzeApiKey');
Enter fullscreen mode Exit fullscreen mode

#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}`);
    });
}
Enter fullscreen mode Exit fullscreen mode

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}`);
}
Enter fullscreen mode Exit fullscreen mode

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}`);
Enter fullscreen mode Exit fullscreen mode

#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}`);
      }
   });
}
Enter fullscreen mode Exit fullscreen mode

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');
   }));
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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
    });
}
Enter fullscreen mode Exit fullscreen mode

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
   }
});
Enter fullscreen mode Exit fullscreen mode

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
   }
});
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
mmueller profile image
Michael Müller

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.

Collapse
 
cteyton profile image
Cédric Teyton

Thanks Michael,
I won't hesitate to let you know if I'll work on it in the future ;)

Collapse
 
fyodorio profile image
Fyodor

Very cool, thanks 👍

Collapse
 
cteyton profile image
Cédric Teyton

You're welcome !