-
Notifications
You must be signed in to change notification settings - Fork 33
Expand file tree
/
Copy pathextension-development.qmd
More file actions
395 lines (290 loc) · 13.8 KB
/
extension-development.qmd
File metadata and controls
395 lines (290 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
---
title: "Extension Development"
description: "Build VS Code-compatible extensions for Positron. Create custom tools using TypeScript, Node.js, and the @posit-dev/positron npm package."
---
Positron is compatible with VS Code extensions so you can create extensions [as you would for VS Code](https://code.visualstudio.com/api/get-started/your-first-extension). You can use Positron to develop your extension and run it in a new **Extension Development Host** window.
## Prerequisites
To follow these docs and run the examples, you'll need:
- Basic familiarity with TypeScript/JavaScript and npm
- An up-to-date version of Positron installed
- Node.js (version 18 or greater)
These docs use the [@posit-dev/positron](https://www.npmjs.com/package/@posit-dev/positron) npm package for type safety and IntelliSense.
The easiest way to follow along is to download the [Positron extension template](https://github.com/posit-dev/positron-extension-template) and add the examples to it as needed.
For more comprehensive setup instructions see the [VS Code documentation on developing an extension.](https://code.visualstudio.com/api/get-started/your-first-extension)
## What can you build?
The Positron API provides access to Positron-specific features like running code in active Python or R sessions, utilizing Positron's preview pane, or contributing new database connections.
The following are the different sections of the API and how they're used.
| Namespace | Typical use case |
| ---------------------- | ------------------------------------------------------------------------ |
| `positron.window` | Preview URLs and HTML, monitor console layout |
| `positron.runtime` | Execute Python or R code in the active console, enumerate sessions, inspect variables |
| `positron.connections` | Add database / service drivers with custom credential UI |
| `positron.languages` | Provide statement range & help topic providers for new languages |
| `positron.methods` | Call low-level RPCs or show purpose-built dialogs |
| `positron.environment` | Inspect environment variable contributions from all installed extensions |
## Setting up your extension
The easiest way to start an extension is to use the [Positron extension template](https://github.com/posit-dev/positron-extension-template).
Alternatively, you can use VS Code's [extension template system](https://code.visualstudio.com/api/get-started/your-first-extension) to scaffold out a plain VS Code extension and then add Positron functionality to that by installing the Positron npm package.
```bash
npm install @posit-dev/positron
```
### Key files
There are two files that are critical when setting up your extension.
#### Extension script (`extension.ts`)
```typescript
import * as vscode from 'vscode';
import { tryAcquirePositronApi } from '@posit-dev/positron';
export function activate(context: vscode.ExtensionContext) {
// Register a command
const disposable = vscode.commands.registerCommand('myExtension.helloWorld', () => {
const positron = tryAcquirePositronApi();
if (positron) {
vscode.window.showInformationMessage('Hello from Positron!');
// Use Positron features here
} else {
vscode.window.showInformationMessage('Hello from VS Code!');
}
});
context.subscriptions.push(disposable);
}
export function deactivate() {}
```
The `activate()` function is run when your extension is [activated.](https://code.visualstudio.com/api/references/activation-events)
It's where you setup the code behind commands and other extension contributions.
#### Extension manifest (`package.json`)
```json
{
"name": "my-extension",
"displayName": "My Extension",
"version": "0.0.1",
"engines": {
"vscode": "^1.74.0"
},
"activationEvents": [],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "myExtension.helloWorld",
"title": "Hello World"
}
]
},
"dependencies": {
"@posit-dev/positron": "^0.1.0"
}
}
```
Your extension's `package.json` file uses custom extension fields to declare what your extension contributes. It's important to note that you need to add the contribution (in this case the command `myExtension.helloWorld`) to _both_ the Typescript/Javascript code and the `package.json`.
Now users can run your command from the Command Palette via {{< kbd mac=Command-Shift-P win=Ctrl-Shift-P linux=Ctrl-Shift-P >}}.
### Detecting Positron
You have two ways to detect if your extension is running in Positron:
#### Option 1: Context keys
Use the `isPositron` context key in your extension manifest for simple detection:
```json
"commands": [
{
"category": "My Extension",
"command": "myExtension.myCommand",
"title": "My Extension Command",
"enablement": "isPositron"
}
]
```
This enables commands, keybindings, and menu items to be visible _only_ in Positron.
#### Option 2: Positron API
Detect Positron in your code:
```typescript
import { tryAcquirePositronApi } from '@posit-dev/positron';
const positron = tryAcquirePositronApi();
if (positron) {
// Positron features are available with full type safety
}
```
Alternatively, if you just need to check but don't need the API you can use `inPositron()`:
```typescript
import { inPositron } from '@posit-dev/positron';
if (inPositron()) {
// Behavior specific for Positron
}
```
## Examples
Each example shows how to use a Positron feature inside a command.
:::{.callout-note}
Prefer to look at examples in a full extension context? Check out the [Positron API showcase extension.](https://github.com/posit-dev/positron-api-showcase)
:::
### Display a web page
```typescript
const disposable = vscode.commands.registerCommand('myExtension.showWebPage', () => {
const positron = tryAcquirePositronApi();
if (positron) {
const url = vscode.Uri.parse('https://example.com');
positron.window.previewUrl(url);
}
});
```
Add this command to your package.json:
```json
{
"command": "myExtension.showWebPage",
"title": "Show Web Page"
}
```
### Run code in the Console
Execute code in the active Python or R session:
```typescript
const disposable = vscode.commands.registerCommand('myExtension.runCode', () => {
const positron = tryAcquirePositronApi();
if (positron) {
// Run Python code
positron.runtime.executeCode('python', 'print("Hello from extension!")', true);
// Or run R code
// positron.runtime.executeCode('r', 'print("Hello from R")', true);
}
});
```
The third parameter (`true`) means the code appears in the console history.
::: {.callout-important}
The `executeCode` API should be used with caution because R and Python both have the ability to run arbitrary code with the user's full permissions.
Avoid building code strings from untrusted inputs, including those under indirect control (e.g., filenames). If you must incorporate untrusted strings into the code string, ensure that they are escaped properly.
:::
### Add a database connection
Register a custom database type with a working mock example:
```typescript
// In your activate function
export function activate(context: vscode.ExtensionContext) {
const positron = tryAcquirePositronApi();
if (positron) {
const disposable = positron.connections.registerConnectionDriver({
driverId: 'mockdb',
metadata: {
languageId: 'python',
name: 'Mock Database',
inputs: [
{
id: 'database',
label: 'Database Name',
type: 'string',
value: 'test_db'
}
]
},
generateCode: (inputs) => {
// If any of the inputs are an ID, use that as the name
const dbName = inputs.find(i => i.id === 'database')?.value || 'test_db';
return mockDatabaseCode(dbName);
},
connect: async (code) => {
await positron.runtime.executeCode('python', code, true);
}
});
context.subscriptions.push(disposable);
}
}
```
<details>
<summary>Mock database code</summary>
In this example we simulate a database connection with a basic Python class. Swap this logic out for your use case!
```typescript
// Mock database code generator.
const mockDatabaseCode = (dbName: string) => `
# Mock database connection
class MockDatabase:
def __init__(self, name):
self.name = name
self.tables = ['users', 'products', 'orders']
print(f"Connected to mock database: {name}")
print(f"Available tables: {', '.join(self.tables)}")
def query(self, sql):
return f"Mock result for: {sql}"
# Create connection
mock_db = MockDatabase("${dbName}")
print("\\nConnection successful! Try: mock_db.query('SELECT * FROM users')")
`;
```
The `mockDatabaseCode` function generates Python code that creates a simple mock database class. This lets you test the connection without installing a real database.
</details>
### Advanced examples
These examples demonstrate more advanced integrations with Positron's data science features:
#### Stream output with observers
Capture and process output from long-running computations as they execute:
```typescript
import * as vscode from "vscode";
export async function runLongTask(code: string) {
const positron = tryAcquirePositronApi();
if (!positron) return;
// Create an observer to handle output events
const observer = {
onStarted: () => vscode.window.showInformationMessage("Analysis started"),
onOutput: (message) => {
// Process output chunks as they arrive
console.log("Output:", message.text || message);
},
onError: (error) => {
console.error("Runtime error:", error);
vscode.window.showErrorMessage(`Error: ${error.message}`);
},
onFinished: () => vscode.window.showInformationMessage("Analysis complete"),
};
// Execute code with streaming output
await positron.runtime.executeCode(
"python",
code,
true, // show in console
false, // not an evaluation
undefined,
undefined,
observer
);
}
```
Use observers to process output progressively without blocking the UI. This approach works especially well for monitoring long-running analyses or streaming results.
## Best practices
### Performance
* Batch API calls when possible; use `getSessionVariables()` instead of multiple individual lookups.
* All API methods are asynchronous; always use `await` or handle promises appropriately.
### Testing
* The [Positron extension template](https://github.com/posit-dev/positron-extension-template) has simple testing infrastructure setup for you that can be extended to fit your extension's needs.
* The [Positron showcase extension](https://github.com/posit-dev/positron-api-showcase) has a more fleshed out testing implementation.
* Refer to the [VS Code testing docs](https://code.visualstudio.com/api/working-with-extensions/testing-extension) for more information.
### Resource management
* Clean up event listeners in your extension's `deactivate()` function.
* Add disposables to `context.subscriptions` for automatic cleanup.
### Security
* Set `localResourceRoots` when creating webviews.
* Use content security policies for webviews that load external content.
* Never expose sensitive data in webview messages.
## Version compatibility
You can ensure your extension only activates in versions of Positron that you explicitly support by declaring a suitable version range in your `package.json`.
### Positron versioning
Positron follows a calendar versioning scheme, while VS Code uses semantic versioning. Because of this, common range operators such as the caret (`^`) behave differently on each platform:
* **VS Code**: `major.minor.patch`
* A caret range stays within the current major version; `^1.74.0` expands to `>=1.74.0` and `<2.0.0`.
* Stepping up to `2.0.0` can therefore introduce breaking changes.
* **Positron**: `YYYY.M.P` (year · month · patch)
* Under calendar versioning, the caret simply means "this version or anything newer"; `^2025.6.0` translates to `>=2025.6.0` with no upper bound.
* Entering a new year (`2026.x.x`) is *not* automatically breaking—always consult the release notes.
### Specifying Positron version requirements
Add a `positron` entry under the `engines` section of your `package.json` to declare the minimum build that your extension supports.
```json
{
"engines": {
"vscode": "^1.74.0", // VS Code 1.74.0 or later
"positron": "^2025.6.0" // Positron 2025.6.0 or later
}
}
```
### `@posit-dev/positron` versioning
You can check the [version compatibility table](https://github.com/posit-dev/positron-api-pkg?tab=readme-ov-file#version-compatibility) in the `@posit-dev/positron` package to check for any breaking changes you may need to be aware of.
## Next steps
### Resources
- Start quickly with the [Positron extension template](https://github.com/posit-dev/positron-extension-template).
- See more examples of the available API in the [showcase extension](https://github.com/posit-dev/positron-api-showcase).
- Complete type documentation is available on [npm](https://www.npmjs.com/package/@posit-dev/positron).
- Browse the source code of the [API package repository](https://github.com/posit-dev/positron-api-pkg).
### Get help
- Ask questions in [GitHub discussions](https://github.com/posit-dev/positron/discussions).
- File bugs in the [API repository](https://github.com/posit-dev/positron-api-pkg/issues).
::: {.callout-note}
Start with the extension template to get a working setup quickly. Then add features one at a time as you learn the API.
:::