Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
"${workspaceFolder}/packages/vscode-messenger-devtools/lib/**/*.js",
"${workspaceFolder}/packages/vscode-messenger/lib/**/*.js",
],
"debugWebviews": true,
"rendererDebugOptions": {
"webRoot": "${workspaceFolder}/packages/vscode-messenger-devtools/webview-ui",
"outFiles": [
"${workspaceFolder}/packages/vscode-messenger-devtools/webview-ui/build/**/*.js"
]
},
"preLaunchTask": "Watch All"
}
]
Expand Down
177 changes: 120 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,88 +7,151 @@ RPC messaging library for the VS Code extension platform. Makes the communicatio
[![License](https://img.shields.io/github/license/TypeFox/vscode-messenger?color=green)](https://github.com/TypeFox/vscode-messenger/blob/main/LICENSE)
[![Codespaces](https://img.shields.io/badge/Codespaces-Open-blue?logo=github)](https://codespaces.new/TypeFox/vscode-messenger)

#### Supported features

- Sending notification or a request from an __extension to a view__, a __view group__ or __broadcast__ to all registered views
- Sending notification or a request from a __view to other view__, a view group or the host extension
- Support for __sync and async__ request/notification handlers
- Support for __request cancellation__
- __Typed__ API
- Automatically unregister views on view dispose
## Features

- Send notifications or requests from an **extension to a view**, a **view group**, or **broadcast** to all registered views
- Send notifications or requests from a **view to another view**, a view group, or the host extension
- **Typed API** — `RequestType<P, R>` and `NotificationType<P>` enforce matching parameter and result types on both sides
- Sync and async request/notification handlers
- Request cancellation
- Automatically unregisters views on dispose
- Configurable logging

#### Diagnostics vs-code extension
## Packages

[![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/typefox.vscode-messenger-devtools?label=VS-Code%20Marketplace)](https://marketplace.visualstudio.com/items?itemName=typefox.vscode-messenger-devtools)
Install only what each side needs:

| Package | Install in | Purpose |
|---|---|---|
| [`vscode-messenger`](https://www.npmjs.com/package/vscode-messenger) | Extension host | `Messenger` class, view registration, diagnostic API |
| [`vscode-messenger-webview`](https://www.npmjs.com/package/vscode-messenger-webview) | Webview script | `Messenger` class for webview, `createCancellationToken` |
| [`vscode-messenger-common`](https://www.npmjs.com/package/vscode-messenger-common) | Shared module | `NotificationType`, `RequestType`, `HOST_EXTENSION`, `BROADCAST` |

[Devtool vscode extension](https://github.com/TypeFox/vscode-messenger/tree/main/packages/vscode-messenger-devtools) helps inspecting messages interaction between your extension components.
**Tip:** Keep all `NotificationType` / `RequestType` declarations in a shared TypeScript module imported by both the extension and the webview. The same `method` string and parameter/result types must match on both ends.

#### Usage in an extension (TS example)
## Usage in an extension (TypeScript)

```ts
import * as vscode from 'vscode';
import { Messenger } from 'vscode-messenger';
import { NotificationType, RequestType } from 'vscode-messenger-common';

const messenger = new Messenger();

// register one or more views
// Register a webview view (or use registerWebviewPanel for WebviewPanel)
messenger.registerWebviewView(webviewView);


// Handle incoming view notification
// Define message types — declare once, import on both sides
const colorSelectType: NotificationType<string> = { method: 'colorSelected' };
const availableColorsType: RequestType<string, string[]> = { method: 'availableColor' };
const colorModifyType: NotificationType<string> = { method: 'colorModify' };

messenger.onNotification(colorSelectType, (params: string) => {
vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(`#${params}`));
// Handle a notification from the webview
messenger.onNotification(colorSelectType, (color: string) => {
vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(`#${color}`));
});

// Handle view requests and return a result
const availableColorsType: RequestType<string, string[]> = { method: 'availableColor' };

messenger.onRequest(availableColorsType, (params: string) => {
// Handle a request from the webview and return a result
messenger.onRequest(availableColorsType, (_params: string) => {
return ['020202', 'f1eeee', 'a85b20', 'daab70', 'efcb99'];
});

// Send a notification to a view of type 'calicoColors.colorsView'
const colorModifyType: NotificationType<string> = { method: 'colorModify' };
// Send a notification to all views of a given type
messenger.sendNotification(colorModifyType, { type: 'webview', webviewType: 'calicoColors.colorsView' }, 'clear');

// Send a request to a view and await the result
const selectedColor = await messenger.sendRequest(
{ method: 'getSelectedColor' },
{ type: 'webview', webviewType: 'calicoColors.colorsView' },
''
);
```

messenger.sendNotification(colorModifyType, {type: 'webview', webviewType: 'calicoColors.colorsView' }, 'clear');
## Usage in a webview (TypeScript)

```ts
import { Messenger } from 'vscode-messenger-webview';
import { HOST_EXTENSION } from 'vscode-messenger-common';
import { colorModifyType, availableColorsType, colorSelectType } from './shared/message-types';

// Send a request to a view of type 'calicoColors.colorsView' and get a result
const selectedColor = await messenger.sendRequest({ method: 'getSelectedColor' }, {type: 'webview', webviewType: 'calicoColors.colorsView' }, '');
```
const messenger = new Messenger(); // acquireVsCodeApi() is called automatically

#### Usage in a webview (JS Example)

Using JS in this example for simplicity. You can use TypeScript as well.

```js
const vscode = acquireVsCodeApi();
const vscode_messenger = require("vscode-messenger-webview");

const messenger = new vscode_messenger.Messenger(vscode);

// Handle extension Notifications. For requests use `onRequest()`
messenger.onNotification({method: 'colorModify'}, (params) => {
switch(params) {
case 'add': {
addColor();
break;
}
case 'clear': {
colors = [];
updateColorList(colors);
break;
}
}
// Handle notifications from the extension
messenger.onNotification(colorModifyType, (action: string) => {
if (action === 'clear') clearColors();
if (action === 'add') addColor();
});
messenger.start(); // start listening for incoming events

// Send a request to your extension.
// For notification use `sendNotification()`
const colors = await messenger.sendRequest({ method: 'availableColors'}, HOST_EXTENSION, '');
console.log(colors);
messenger.start(); // required — starts listening for incoming messages

// Send a request to the host extension
const colors = await messenger.sendRequest(availableColorsType, HOST_EXTENSION, '');

// Send a notification to the host extension
messenger.sendNotification(colorSelectType, HOST_EXTENSION, 'a85b20');
```

> **Note:** `messenger.start()` must be called before any messages can be received. Forgetting it causes all incoming messages to be silently dropped.

## Key concepts

### Message participants

| Participant | How to construct | Use for |
|---|---|---|
| Host extension | `HOST_EXTENSION` | Targeting the extension from a webview |
| Webview by type | `{ type: 'webview', webviewType: '...' }` | All instances of a view type |
| Webview by id | returned by `registerWebviewView` / `registerWebviewPanel` | One specific instance |
| Broadcast | `BROADCAST` | All registered views (notifications only) |

### Request cancellation

```ts
// Extension side — pass any CancellationToken
const cts = new vscode.CancellationTokenSource();
const result = await messenger.sendRequest(longOpType, target, params, cts.token);
cts.cancel();

// Webview side — bridge an AbortSignal
import { createCancellationToken } from 'vscode-messenger-webview';
const ctrl = new AbortController();
const result = await messenger.sendRequest(longOpType, HOST_EXTENSION, params, createCancellationToken(ctrl.signal));
ctrl.abort('timeout');
```

### Broadcast notifications

A webview receives a broadcast only when registered with `broadcastMethods` listing the method:

```ts
// Extension: opt the view in to specific broadcast methods
messenger.registerWebviewView(view, { broadcastMethods: [refreshType.method] });

// Either side: broadcast to all opted-in views
messenger.sendNotification(refreshType, BROADCAST);
```

## Diagnostics and devtools

[![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/typefox.vscode-messenger-devtools?label=VS-Code%20Marketplace)](https://marketplace.visualstudio.com/items?itemName=typefox.vscode-messenger-devtools)

The companion **[VS Code Messenger Developer Tool](https://marketplace.visualstudio.com/items?itemName=typefox.vscode-messenger-devtools)** extension visualizes live message traffic — requests, responses, and notifications — between the extension host and its webviews. To enable it, expose `messenger.diagnosticApi()` from your extension's `activate` return value:

```ts
import { Messenger, type MessengerDiagnostic } from 'vscode-messenger';

const messenger = new Messenger();

export function activate(context: vscode.ExtensionContext): MessengerDiagnostic {
// ... register views, handlers, etc.
return messenger.diagnosticApi();
}
```

#### More examples
Open the devtool with **Developer: Open vscode-messenger devtools** from the Command Palette.

See the [devtools README](packages/vscode-messenger-devtools/README.md) for full setup instructions, `DiagnosticOptions`, the event schema, and troubleshooting.

## More examples

See tests for more examples.
The [calico-colors example](examples/calico-colors) demonstrates a complete extension with a `WebviewView` and a `WebviewPanel` sharing a single `Messenger` instance, typed message definitions in a shared module, broadcast usage, and diagnostic API integration.
3 changes: 2 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ module.exports = {
"no-multiple-empty-lines": ["error", { "max": 1 }], // two or more empty lines need to be fused to one
"no-new-wrappers": "error", // there is no reason to wrap primitve values
"no-throw-literal": "error", // only throw Error but no objects {}
"no-trailing-spaces": "error", // trim end of lines
"no-trailing-spaces": "off", // trim end of lines
"no-unsafe-finally": "error", // safe try/catch/finally behavior
"no-var": "error", // use const and let instead of var
"no-constant-binary-expression": "warn", // warn about expressions like if (a === 1 || 0) which are always true
"space-before-function-paren": ["error", { // space in function decl: f() vs async () => {}
"anonymous": "never",
"asyncArrow": "always",
Expand Down
Loading
Loading