Kogito Tooling Examples — How to create a VS Code Extension for a custom View

Photograph by Renan Ozturk

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:

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:

The ‘To-do’ List View as a VS Code Extension

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
}
}
view raw TodoListWebview.ts hosted with ❤ by GitHub

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)],
});
// …
}
view raw TodoListWebview.ts hosted with ❤ by GitHub

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>`;
// …
}
view raw TodoListWebview.tsx hosted with ❤ by GitHub

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" }
)
);
// …
}
view raw TodoListWebview.ts hosted with ❤ by GitHub

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();
}
view raw TodoListWebview.ts hosted with ❤ by GitHub

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",
view raw package.json hosted with ❤ by GitHub

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"
},
view raw package.json hosted with ❤ by GitHub

Also, it’s important to tell the extension entry point file.

"main": "./dist/extension.js",
view raw package.json hosted with ❤ by GitHub

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"
}
]
},
view raw package.json hosted with ❤ by GitHub

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": [
"*"
],
view raw package.json hosted with ❤ by GitHub

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.

This post was original published on here.
0 0 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments