21 questions you’ll ask if you code a VSCode extension
Tags: vscode, programming, tutorial, typescript
Published: 2023-01-12 09:52:52

Are you about to start developing a **VSCode extension**? Or maybe you struggle with what you’re currently trying to achieve? 

At [Packmind](https://packmind.com), we’ve built a [VSCode extension](https://marketplace.visualstudio.com/items?itemName=ProMyze.promyze-vs-code) to help developers [share their best coding practices](https://www.packmind.com/how-it-works/), 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:

```tsx
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:

```tsx
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:

```tsx
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:

```tsx
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](https://code.visualstudio.com/docs/languages/identifiers).

## #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.

```tsx
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:

```tsx
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:

```tsx
{
   "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:

```tsx
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:

```tsx
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):

```tsx
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:  

```tsx
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.:

```tsx
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:

```tsx
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:

```tsx
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:

```json

{
   "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:

```tsx
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:

```tsx
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:

```tsx
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:

```tsx
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:

```tsx
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):

```tsx
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](https://code.visualstudio.com/docs/getstarted/keybindings). 

## #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:

```json
"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:

```tsx
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:

```tsx
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:

```tsx
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](https://code.visualstudio.com/api/references/vscode-api#workspace).