-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVscode.affine
More file actions
307 lines (244 loc) · 15.8 KB
/
Copy pathVscode.affine
File metadata and controls
307 lines (244 loc) · 15.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
// SPDX-License-Identifier: PMPL-1.0-or-later
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//
// Vscode.affine — issue #35 Phase 2 bindings for the VS Code extension API.
//
// All host objects (Disposable, Terminal, ExtensionContext, ...) are opaque
// handles represented as Int. The JS-side shim (see packages/affine-vscode/
// or whatever your Node-CJS extension's _extraImports() returns) maintains a
// per-process JS handle table and translates between Int handles and live JS
// objects when each `extern fn` is invoked.
//
// Convention: every binding is `extern fn <name>(...) -> ...;` and the
// Wasm import lands under the `env` namespace (matches what the Node-CJS
// shim's import map expects). The function names match the vscode API
// where possible; nested namespaces are flattened (e.g.
// `vscode.commands.registerCommand` → `registerCommand`).
//
// API surface inventoried in issue #35: the ten symbols actually used by
// the AffineScript and rattlescript-face VS Code extensions today. Add
// further bindings on demand — keep this file's growth proportional to
// real consumers.
//
// Surface expanded 2026-05-11 for the rsr-certifier port (affinescript#64
// — Port external VS Code extensions). Additions cover the workspace,
// file-system, status-bar, diagnostics, webview, clipboard, and path
// surfaces actually used by rhodium-standard-repositories/.../rsr-certifier.
// Two API shapes were deliberately omitted from this expansion because
// they cannot be expressed in the current synchronous extern-call ABI:
// - vscode.window.withProgress(opts, async task)
// - LanguageClient.sendRequest(method, params) (returns Thenable)
// Extensions that need those features should fall back to terminal /
// child_process shells until an async-extern ABI lands.
module Vscode;
// ── Opaque host-supplied handle types ──────────────────────────────────
pub extern type Disposable;
pub extern type WorkspaceConfiguration;
pub extern type FileSystemWatcher;
pub extern type TextEditor;
pub extern type Terminal;
pub extern type Thenable;
pub extern type ExtensionContext;
pub extern type StatusBarItem;
pub extern type DiagnosticCollection;
pub extern type WebviewPanel;
pub extern type Uri;
pub extern type TextDocument;
// ── vscode.commands ───────────────────────────────────────────────────
/// `vscode.commands.registerCommand(name, handler)`.
/// `handler` is a function value (#199 function-value callback ABI). The
/// `Unit` parameter is explicit because AffineScript types a zero-param
/// function as its bare return type — `fn() -> Int` would collapse to
/// `Int`; `fn(Unit) -> Int` is the honest callback shape.
pub extern fn registerCommand(name: String, handler: fn(Unit) -> Int) -> Disposable;
// ── vscode.workspace ──────────────────────────────────────────────────
pub extern fn getConfiguration(section: String) -> WorkspaceConfiguration;
/// `cfg.get<String>(key, default)` — current binding only supports the
/// String-returning shape. Numeric / boolean variants can be added with
/// per-type extern fns when needed.
pub extern fn workspaceConfigGetString(cfg: WorkspaceConfiguration,
key: String,
default_value: String) -> String;
pub extern fn createFileSystemWatcher(glob: String) -> FileSystemWatcher;
// ── vscode.window ─────────────────────────────────────────────────────
/// Returns 0 if there is no active text editor (host returns a sentinel
/// handle of 0 in the absent case).
pub extern fn activeTextEditor() -> TextEditor;
pub extern fn showErrorMessage(msg: String) -> Thenable;
pub extern fn showWarningMessage(msg: String) -> Thenable;
pub extern fn showInformationMessage(msg: String) -> Thenable;
pub extern fn createTerminal(name: String) -> Terminal;
pub extern fn terminalShow(t: Terminal) -> Int;
pub extern fn terminalSendText(t: Terminal, text: String) -> Int;
// ── ExtensionContext ──────────────────────────────────────────────────
/// `context.subscriptions.push(disposable)` — registers a Disposable
/// for cleanup when the extension is deactivated.
pub extern fn pushSubscription(ctx: ExtensionContext, d: Disposable) -> Int;
// ── Editor document helpers ───────────────────────────────────────────
//
// These collapse `vscode.window.activeTextEditor.document.{uri.fsPath,languageId}`
// into single calls because AffineScript doesn't have a chained-property
// accessor for opaque host objects yet — a per-leaf extern is the natural
// shape until that lands.
/// Path of the active editor's open file (empty string if no active editor).
pub extern fn editorActiveFilePath() -> String;
/// Language ID of the active editor's open document (empty string if none).
pub extern fn editorActiveLanguageId() -> String;
// ── Boolean configuration values ──────────────────────────────────────
/// `cfg.get<boolean>(key, default)` returning 0/1.
pub extern fn workspaceConfigGetBool(cfg: WorkspaceConfiguration,
key: String,
default_value: Int) -> Int;
// ── Host process / IO ────────────────────────────────────────────────
/// `console.log(msg)` for activation logging.
pub extern fn consoleLog(msg: String) -> Int;
/// Synchronous `child_process.execSync` wrapper — returns the exit code
/// (0 = success). Used to probe whether a command is on PATH.
pub extern fn execSync(cmd: String) -> Int;
// ── String helpers ────────────────────────────────────────────────────
//
// These lift JS String ops to AffineScript so the extension can build
// terminal command lines without needing AffineScript's own String
// primitives (which are still WIP). All return new strings; inputs are
// not mutated.
pub extern fn stringConcat(a: String, b: String) -> String;
pub extern fn stringEndsWith(s: String, suffix: String) -> Int;
pub extern fn stringReplaceSuffix(s: String,
suffix: String,
replacement: String) -> String;
/// Returns 1 if `s` has zero length, 0 otherwise. Useful for testing the
/// "no result" sentinel returned by getters like `workspaceFolderFirstPath`
/// without needing a String=String primitive in the AffineScript surface.
pub extern fn stringIsEmpty(s: String) -> Int;
// ── Workspace ─────────────────────────────────────────────────────────
/// First workspace folder's `uri.fsPath`, or "" if there is no workspace.
/// Equivalent to `vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? ""`.
pub extern fn workspaceFolderFirstPath() -> String;
/// First workspace folder as a `vscode.Uri`. Used as the `base` for
/// `uriJoinPath` when building paths under the workspace root. Returns a
/// sentinel handle of 0 when there is no workspace.
pub extern fn workspaceRootUri() -> Uri;
// ── URI / file-system / text documents ───────────────────────────────
/// `vscode.Uri.file(path)` — wraps a filesystem path as a Uri handle.
pub extern fn uriFromPath(path: String) -> Uri;
/// `vscode.Uri.joinPath(base, segment)` — single-segment join only;
/// callers that need multiple segments should chain.
pub extern fn uriJoinPath(base: Uri, segment: String) -> Uri;
/// `uri.fsPath` — returns the filesystem path the Uri represents.
pub extern fn uriPath(u: Uri) -> String;
/// `vscode.workspace.fs.writeFile(uri, Buffer.from(content))`. The write
/// is dispatched to the VS Code FS API; the binding returns 0 on a clean
/// dispatch and a non-zero error code otherwise. The underlying write
/// returns a Thenable — the binding does not await it. Best-effort by
/// design (sufficient for the rsr.initConfig use-case which immediately
/// opens the file for the user to inspect).
pub extern fn fsWriteFile(u: Uri, content: String) -> Int;
/// `vscode.workspace.openTextDocument(uri)` — likewise best-effort
/// dispatch; the returned handle is valid for `showTextDocument`.
pub extern fn openTextDocument(u: Uri) -> TextDocument;
/// `vscode.window.showTextDocument(doc)`.
pub extern fn showTextDocument(d: TextDocument) -> Int;
// ── Status bar ───────────────────────────────────────────────────────
/// `vscode.window.createStatusBarItem(alignment, priority)`.
/// `alignment`: 0 = Left, 1 = Right (matches `vscode.StatusBarAlignment`).
pub extern fn createStatusBarItem(alignment: Int, priority: Int) -> StatusBarItem;
pub extern fn statusBarItemSetText(s: StatusBarItem, text: String) -> Int;
pub extern fn statusBarItemSetTooltip(s: StatusBarItem, tooltip: String) -> Int;
pub extern fn statusBarItemSetCommand(s: StatusBarItem, command_id: String) -> Int;
/// Sets `statusBarItem.backgroundColor` to a `new vscode.ThemeColor(name)`.
/// An empty `theme_color` clears the background (sets it to `undefined`).
pub extern fn statusBarItemSetBackgroundColorTheme(s: StatusBarItem,
theme_color: String) -> Int;
pub extern fn statusBarItemShow(s: StatusBarItem) -> Int;
pub extern fn statusBarItemHide(s: StatusBarItem) -> Int;
/// Reinterprets a `StatusBarItem` as a `Disposable` so it can be pushed
/// onto `context.subscriptions`. (The vscode-API contract: status-bar
/// items implement Disposable.)
pub extern fn statusBarItemAsDisposable(s: StatusBarItem) -> Disposable;
// ── Diagnostics ──────────────────────────────────────────────────────
//
// To avoid an FFI for `Range` and `Diagnostic` constructors plus an array
// type, diagnostics are passed as a JSON array of objects:
//
// [
// {
// "message": "...",
// "severity": 0..3, // Error|Warning|Information|Hint
// "startLine": 0,
// "startCol": 0,
// "endLine": 0,
// "endCol": 0
// },
// ...
// ]
//
// The adapter parses the JSON and constructs `vscode.Diagnostic`
// instances. Range fields are required; sentinel `0,0,0,0` is the
// established convention for "no specific location, attach to the file
// as a whole" (matches what the rsr-certifier TS extension did).
pub extern fn createDiagnosticCollection(name: String) -> DiagnosticCollection;
pub extern fn diagnosticCollectionClear(c: DiagnosticCollection) -> Int;
/// `c.set(uri, parseDiagnosticsJson(diagnostics_json))`. Returns 0 on
/// success or a parse-error sentinel (non-zero) if `diagnostics_json`
/// failed to parse.
pub extern fn diagnosticCollectionSetForUri(c: DiagnosticCollection,
u: Uri,
diagnostics_json: String) -> Int;
/// Returns a `Disposable` for `c` so it can join `context.subscriptions`.
pub extern fn diagnosticCollectionAsDisposable(c: DiagnosticCollection) -> Disposable;
// ── Webview ──────────────────────────────────────────────────────────
/// `vscode.window.createWebviewPanel(viewType, title, viewColumn, {})`.
/// `view_column`: 1 = One, 2 = Two, 3 = Three (matches
/// `vscode.ViewColumn`). No webview options exposed in this first cut —
/// the default (no scripts, no retained context) is what rsr-certifier's
/// existing report uses.
pub extern fn createWebviewPanel(view_type: String,
title: String,
view_column: Int) -> WebviewPanel;
/// Assigns to `panel.webview.html`.
pub extern fn webviewPanelSetHtml(p: WebviewPanel, html: String) -> Int;
pub extern fn webviewPanelAsDisposable(p: WebviewPanel) -> Disposable;
// ── Clipboard ────────────────────────────────────────────────────────
/// `vscode.env.clipboard.writeText(text)`. Best-effort dispatch; the
/// underlying call returns a Thenable that the binding does not await.
pub extern fn clipboardWriteText(text: String) -> Int;
// ── Events ───────────────────────────────────────────────────────────
/// `vscode.workspace.onDidSaveTextDocument(() => handler())`.
///
/// The handler is registered as a zero-argument wasm-table thunk. The
/// `TextDocument` argument that the underlying event ships is discarded
/// at the FFI boundary; handlers that need the saved file path can call
/// `editorActiveFilePath()` from inside the thunk. That covers the
/// 95% case where the saved doc is the active editor; the remaining
/// cases (background-saved docs) degrade to "no action".
pub extern fn onDidSaveTextDocument(handler: fn(Unit) -> Int) -> Disposable;
// ── Path helpers ─────────────────────────────────────────────────────
/// `path.basename(p)` — last segment of a slash- or backslash-separated
/// path.
pub extern fn pathBasename(p: String) -> String;
/// `path.join(a, b)` — single-segment join using the host platform's
/// separator.
pub extern fn pathJoin(a: String, b: String) -> String;
/// `process.platform` — "win32" | "linux" | "darwin" | "freebsd" | ...
pub extern fn processPlatform() -> String;
// ── ExtensionContext helpers ─────────────────────────────────────────
/// `context.asAbsolutePath(rel_path)` — resolves a path relative to the
/// extension's install location.
pub extern fn extensionAbsolutePath(ctx: ExtensionContext, rel_path: String) -> String;
// ── Async-extern ABI (issue #103) ────────────────────────────────────
//
// AffineScript's extern-call shape is synchronous, but most of vscode's
// interactive surface returns a JS Thenable. `Thenable` is an opaque
// host handle; the Deno/Node source-to-source backend lowers a
// Thenable-returning extern to a plain host call, so the emitted JS is a
// real Promise and a consumer's `await` is valid source JS. The `Async`
// effect (v1 registry, #59) tracks this in signatures. The vscode-host
// JS implementation lives in packages/affine-vscode/mod.js (follow-up
// slice); these declarations are the binding surface + effect tracking.
// `Thenable` is already declared in the opaque-type block above; no
// re-declaration here (the duplicate from #198 is removed at source).
/// `vscode.window.withProgress({ location: Notification, title }, task)`.
/// `work` is the task function (#199 function-value callback ABI; the
/// explicit `Unit` param avoids the zero-param-fn return-type collapse).
/// Returns the progress Thenable.
pub extern fn withProgressNotification(title: String, work: fn(Unit) -> Thenable) -> Thenable / Async;