
This is part of a series of blog posts, and this one covers “How to create a custom View”.
You can navigate through the series by clicking on the following topics:
- How to create a custom Editor in a React application;
- How to create a Chrome Extension for a custom Editor;
- How to create a VS Code Extension for a custom Editor;
- How to create a custom View;
- How to create a VS Code Extension for a custom View;
- How to create a more complex custom View;
- How to integrate a custom Editor, an existing Editors, and custom Views on a Web App;
This section will cover how to create a VS Code Extension for our ‘To-do’ List View created in the previous example. Before we start, it’s recommended to access the VS Code Extension API reference to be aware of the VS Code Extension capabilities.
Here’s is a quick peek of how the extension will look like:
Obs: This example requires the VS Code version 1.46 or later.
Starting
Firstly, we’re going to create a new submodule called vscode and inside it we’ll create a new file responsible for opening a Webview on the VS Code, which will basically be a new tab with our ‘To-do’ List View. We’ll name this file as TodoListWebview.ts.
We’ll build the class where we receive all the useful properties to set up the communication between the Webview (inside the Envelope) and the Extension. Also, we set up the location of the Envelope to be rendered. The open method will be used by the activate function on the extension.ts file, and it is responsible for opening the Webview on the new tab.
import * as vscode from "vscode"; | |
import { TodoListChannelApi } from "../api"; | |
export class TodoListWebview { | |
constructor( | |
private readonly context: vscode.ExtensionContext, | |
private readonly envelopeLocator: { | |
targetOrigin: string; | |
title: string; | |
envelopePath: string; | |
}, | |
private readonly api: TodoListChannelApi | |
) {} | |
public open(pageId: string, opts: { onClose: () => void }) { | |
// Open code here | |
} | |
} |
In the open method, we create a Webview utilizing the vscode library.
public open(pageId: string, opts: { onClose: () => void }) { | |
const webviewPanel = vscode.window.createWebviewPanel(pageId, this.envelopeLocator.title, ViewColumn.Beside, { | |
retainContextWhenHidden: true, | |
enableCommandUris: true, | |
enableScripts: true, | |
localResourceRoots: [vscode.Uri.file(this.context.extensionPath)], | |
}); | |
// … | |
} |
After that, we create the base HTML code to be used by the Envelope and add the Envelope code to the script tag.
public open(pageId: string, opts: { onClose: () => void }) { | |
// … | |
const scriptSrc = webviewPanel.webview | |
.asWebviewUri(Uri.file(this.context.asAbsolutePath(this.envelopeLocator.envelopePath))) | |
.toString(); | |
webviewPanel.webview.html = `<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<style> | |
html, body, div#envelope-app { | |
margin: 0; | |
border: 0; | |
padding: 10px; | |
} | |
</style> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
</head> | |
<body> | |
<div id="envelope-app" /> | |
<script src="${scriptSrc}"></script> | |
</body> | |
</html>`; | |
// … | |
} |
To communicate with the Extension, we create an EnvelopeServer typed with the Channel and Envelope API. Here we pass down the method used to send messages, the origin, and the initial method (like the pollInit on the EmbeddedTodoList).
public open(pageId: string, opts: { onClose: () => void }) { | |
// … | |
const envelopeServer: EnvelopeServer<TodoListChannelApi, TodoListEnvelopeApi> = new EnvelopeServer( | |
{ postMessage: (message) => webviewPanel.webview.postMessage(message) }, | |
this.envelopeLocator.targetOrigin, | |
() => | |
envelopeServer.manager.clientApi.requests.todoList__init( | |
{ origin: envelopeServer.origin, envelopeServerId: envelopeServer.id }, | |
{ user: "Tiago" } | |
) | |
); | |
// … | |
} |
Wrapping up, we set up the EnvelopeServer as the receiver of the messages coming from the Extension, and we register a clean up callback to be used when the Webview is closed. Of course, we can’t forget to start the EnvelopeServer and return the client to the Extension to communicate with the Envelope.
public open(pageId: string, opts: { onClose: () => void }) { | |
// … | |
this.context.subscriptions.push( | |
webviewPanel.webview.onDidReceiveMessage( | |
(msg) => envelopeServer.receive(msg, this.api), | |
webviewPanel.webview, | |
this.context.subscriptions | |
) | |
); | |
webviewPanel.onDidDispose( | |
() => { | |
envelopeServer.stopInitPolling(); | |
opts.onClose(); | |
}, | |
webviewPanel.webview, | |
this.context.subscriptions | |
); | |
envelopeServer.startInitPolling(); | |
} |
Extension Code
Like the example where we create a VS Code Extension for a custom Editor, we’ll need the some files:
- package.json
- src/envelope/index.ts
- src/extension.ts
package.json
The package.json is the manifest file of the VS Code Extension and has fundamental setups. You can find the package.json file here.
It’s important to specify the published and name properties because both combined create the extension name.
"publisher": "kogito-tooling-examples", | |
"name": "todo-list-view-vscode-extension", |
As mentioned before, we require a VS Code version 1.46 or later, and we specify it using the engine property.
"engines": { | |
"vscode": "^1.46.0" | |
}, |
Also, it’s important to tell the extension entry point file.
"main": "./dist/extension.js", |
Now we’re going to inform the extension contribution points, which are responsible for adding commands to be used with the command palette (CTRL + SHIFT + P) or add new options into the file menus.
"contributes": { | |
"menus": { | |
"editor/context": [ | |
{ | |
"command": "kogito-tooling-examples.todo-list-view.add-item" | |
} | |
] | |
}, | |
"commands": [ | |
{ | |
"command": "kogito-tooling-examples.todo-list-view", | |
"title": "Open list", | |
"category": "TODO" | |
}, | |
{ | |
"command": "kogito-tooling-examples.todo-list-view.add-item", | |
"title": "TODO: Add item(s)" | |
}, | |
{ | |
"command": "kogito-tooling-examples.todo-list-view.mark-all-as-completed", | |
"title": "Mark all as completed", | |
"category": "TODO" | |
} | |
] | |
}, |
We’ve added three new commands/menu options:
- todo-list-view.add-item: To add a new item into our list, it can be used with the command pallet and the file options.
- todo-list-view: To visualize our list (exclusively from the command palette).
- mark-all-as-completed: To mark all items of the list as completed (exclusively from the command palette).
To finalize, we specify when the Extension will be active with the activationEvent property.
"activationEvents": [ | |
"*" | |
], |
src/envelope/index.ts
This file is responsible for the initialization of the Envelope. We set the container to the envelope-app of the TodoListWebview created, and we pass the acquireVsCodeApi to the bus property. This method is responsible for sending messages from a Webview to an Extension.
src/extension.ts
Like it was set on the package.json main property, this file will be the Extension entry point. Here we define two important methods: activate and deactivate.
The activate method will be called when the condition on the package.json activationEvents property is met.
The deactivate method will be called when the activationEvents condition isn’t met anymore.
In this Extension, the activation method is responsible for registering all commands specified on the contribution points and for starting the TodoListWebview with the Envelope path.
It isn’t necessary to clean up when the activationEvent isn’t met anymore, so the deactivate method is empty. You can check the entire file here.
Running
To run this Extension, you can use the vsce package to generate a vsix file and manually install it (read more about here), or you can run it in debug mode.
To run in debug mode, we need to create two files in the .vscode directory. The launch.json, which is the primary file used for debugging, and task.json, create a task used by the launch.json. The launch.json is executed when you open the VS Code Extension folder on utilizing the VS Code Editor and start the debug mode (use the F5 shortcut). You can check our setup on the repository used by this example.
Wrapping up
Now you know how to create a VS Code Extension with your custom View! In the next section, we’ll show you how to create a more complex View, so if you want, you’ll be able to use another framework instead of React.
Thanks for reading, and see you in the next section.