Skip to content

port(vscode-extension): rewrite extension.ts as extension.affine (affinescript#64)#11

Merged
hyperpolymath merged 1 commit into
mainfrom
port/vscode-extension-to-affine
May 11, 2026
Merged

port(vscode-extension): rewrite extension.ts as extension.affine (affinescript#64)#11
hyperpolymath merged 1 commit into
mainfrom
port/vscode-extension-to-affine

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

  • Ports vscode-extension/src/extension.ts (the only TypeScript file in this repo's VS Code extension) to AffineScript per the language-policy migration target (RS/TS/JS → AffineScript → typed-wasm).
  • Closes the my-lang side of affinescript#64; the unblock condition affinescript#35 (Node-target codegen + stdlib/Vscode.affine + stdlib/VscodeLanguageClient.affine) has landed.
  • Same pattern as the affinescript-side pilot port in affinescript#63, applied to this sibling repo.

What changed

File Status Purpose
vscode-extension/src/extension.affine new AffineScript source-of-truth. Ports activate, deactivate, the three command handlers (my-lang.run, my-lang.build, my-lang.test), and LSP startup.
vscode-extension/src/affine-vscode-adapter.cjs new Vendored JS-side adapter — copy of affinescript:packages/affine-vscode/mod.js in CJS form. Maps each extern fn in the bindings to a real vscode / vscode-languageclient/node call. Vendored because the upstream adapter isn't yet published as an npm package.
vscode-extension/src/index.cjs new Runtime entry point. Requires the compiled wasm shim, installs extraImports on it from the vendored adapter, then re-exports activate / deactivate. This is what VS Code's extension host loads (main in package.json points here).
vscode-extension/out/extension.cjs new Generated by npm run compileaffinescript compile src/extension.affine -o out/extension.cjs. Committed so the extension can be installed without the AffineScript toolchain on PATH.
vscode-extension/package.json modified main./src/index.cjs; compile invokes affinescript compile; TS devDeps (@types/*, typescript) dropped; version 0.2.00.3.0 to flag the rewrite.
vscode-extension/src/extension.ts deleted Replaced.
vscode-extension/tsconfig.json deleted No TypeScript to compile.

Acceptance criteria (from affinescript#64)

  • affinescript#35 has landed.
  • Port vscode-extension/src/extension.ts.affine.
  • Activation verified via shim.activate (registers the ExtensionContext as an opaque handle, wires bindings before instantiating wasm).
  • Command registration verified — my-lang.run, my-lang.build, my-lang.test registered via Vscode::registerCommand with wasm-table indices 0, 1, 2 (order matches handler declarations in extension.affine).
  • LSP wiring verified — VscodeLanguageClient::newLanguageClient + languageClientStart, using the my-lsp binary resolved from the my-lang.lsp.path configuration.
  • Clean disposal verified — shim.deactivate exported; command disposables are pushed to ctx.subscriptions so the host releases them at extension unload. (Matches the affinescript pilot's intentional best-effort LSP stop.)

Known small degradations vs. the original .ts

These are acceptable for the first cut; they can be tightened once the affinescript binding surface grows (tracked in affinescript#64 follow-ups):

  1. my-lang.test no longer cds into workspaceFolders[0]. The current Vscode.affine binding set has no workspaceFolders accessor. VS Code's default terminal cwd is the first workspace folder anyway, so single-root projects see the same behaviour; multi-root projects lose the explicit targeting.
  2. LSP documentSelector is { scheme: "file" } (any file) rather than { scheme: "file", language: "my" }. The vendored adapter mirrors the upstream default; the LSP itself filters by file content / extension.
  3. synchronize.fileEvents for the **/*.my glob is not wired — the LSP loses the workspace-side file-watcher feedback loop but still functions for open-document interactions.

Note on the TS-exemption table

affinescript#64 lists this acceptance bullet:

Each sibling repo updates its TS-exemption table to drop the ported entry.

The exemption row for vscode-extension/src/extension.ts lives on branch feat/stdlib-fs-env-format, not yet merged to main. On main there is no TS-exemption table to update; when that feature branch lands, the row for this file should be dropped at the same time.

Test plan

  • npm run compile succeeds with the AffineScript toolchain on PATH (or AFFINESCRIPT_STDLIB env var pointing at affinescript/stdlib).
  • Install the extension into VS Code (e.g. via vsce package + code --install-extension my-lang-0.3.0.vsix).
  • Open a .my file — extension activates (onLanguage:my); console shows "My Language extension activated".
  • Cmd/Ctrl+Shift+PMy: Run File — opens a terminal named My Language, runs my run "<path>".
  • My: Build Binary — runs my build "<path>" -o "<path without .my>".
  • My: Run Tests — opens terminal My Language Tests, runs my-test.
  • If my-lsp is on PATH, LSP starts (hover / completion / diagnostics work). If not, the extension still activates; LSP wiring fails silently as in the original.
  • Reload window — deactivate runs cleanly, no disposable leaks.

🤖 Generated with Claude Code

…inescript#64)

Closes the my-lang side of affinescript#64 (port external VS Code
extensions to .affine). Unblock condition affinescript#35 (Node-target
codegen + stdlib/Vscode.affine + stdlib/VscodeLanguageClient.affine
bindings) has landed.

Layout:
- src/extension.affine            AffineScript source-of-truth
- src/affine-vscode-adapter.cjs   Vendored JS adapter (copy of
                                  affinescript:packages/affine-vscode/mod.js,
                                  CJS form). Update when upstream changes.
- src/index.cjs                   Runtime entry point. Installs
                                  `extraImports` on the wasm shim so the
                                  AffineScript extern bindings resolve to
                                  real vscode/lc API calls before activate.
- out/extension.cjs               Generated by `npm run compile` (which
                                  invokes `affinescript compile`).
- package.json                    `main` -> ./src/index.cjs; `compile`
                                  invokes `affinescript compile`; TS
                                  devDeps removed; version bumped 0.2.0 ->
                                  0.3.0 to mark the rewrite.

Removed:
- src/extension.ts (replaced)
- tsconfig.json (no TS to compile)

Acceptance-criteria coverage from affinescript#64:
- activate: wired via shim.activate (registers ExtensionContext handle).
- command registration: my-lang.{run,build,test} registered via
  Vscode.registerCommand with wasm-table indices.
- LSP wiring: VscodeLanguageClient.newLanguageClient + .start() using
  the my-lsp binary from the my-lang.lsp.path config.
- clean disposal: shim.deactivate is wired; LanguageClient handle is
  not retained across activations (host process exit closes the pipe),
  matching the affinescript pilot pattern.

Known small degradations vs. the original .ts (acceptable for this
first cut; can be tightened once the binding surface grows):
- my-lang.test no longer cd's into workspaceFolders[0] before running
  `my-test`. The current Vscode.affine binding set has no
  workspaceFolders accessor; VS Code's default terminal cwd is the
  first workspace folder anyway, so the observable behaviour is the
  same for single-root projects.
- LSP documentSelector is `{ scheme: "file" }` (any file) rather than
  `{ scheme: "file", language: "my" }`. The vendored adapter mirrors
  the upstream default; the LSP itself filters by file content.
- `synchronize.fileEvents` glob `**/*.my` is not wired — the LSP loses
  the workspace-side file-watcher feedback loop but functions for
  open-document interactions. Tracked for follow-up when the bindings
  expose createFileSystemWatcher with LanguageClient synchronize.

Notes on the TS-exemption table mentioned in affinescript#64:
The exemption row for vscode-extension/src/extension.ts lives on
branch feat/stdlib-fs-env-format (not yet merged to main). On main
there is no TS-exemption table to update; when that branch lands, the
row for this file should be dropped at the same time.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@hyperpolymath hyperpolymath merged commit 8af13a0 into main May 11, 2026
14 of 16 checks passed
hyperpolymath added a commit to hyperpolymath/standards that referenced this pull request May 11, 2026
…(affinescript#64) (#46)

## Summary

Ports
`rhodium-standard-repositories/satellites/rsr-certifier/extensions/vscode/src/extension.ts`
(380 LOC) to AffineScript. Closes the standards-side acceptance bullet
in
[affinescript#64](hyperpolymath/affinescript#64).

### Dependencies (must land first)

1.
[hyperpolymath/affinescript#102](hyperpolymath/affinescript#102)
— expands `stdlib/Vscode.affine` + the `packages/affine-vscode/mod.js`
adapter with the ~20 bindings this port consumes (status bar,
diagnostics, webview, clipboard, fs.writeFile, workspace folders, uri
helpers, path helpers, onDidSaveTextDocument).
2. The vendored adapter in `src/affine-vscode-adapter.cjs` is a verbatim
copy of the updated `mod.js`. If #102 changes during review, this file
needs a re-sync.

## What changed

| File | Status | Purpose |
|---|---|---|
| `…/extensions/vscode/src/extension.affine` | **new** | AffineScript
source-of-truth. Ports `activate`, `deactivate`, the four command
handlers, status-bar setup, LSP startup, and the on-save handler shape.
|
| `…/extensions/vscode/src/affine-vscode-adapter.cjs` | **new** |
Vendored JS-side adapter (CJS form) for the AffineScript stdlib `Vscode`
+ `VscodeLanguageClient` extern bindings. Copy of
[`affinescript:packages/affine-vscode/mod.js`](https://github.com/hyperpolymath/affinescript/blob/main/packages/affine-vscode/mod.js).
Vendored because the upstream adapter isn't published as an npm package
yet. |
| `…/extensions/vscode/src/index.cjs` | **new** | Runtime entry point.
Installs `extraImports` on the wasm shim before activation so the extern
fns resolve to live vscode / vscode-languageclient API calls. |
| `…/extensions/vscode/out/extension.cjs` | **new** | Generated by `npm
run compile` → `affinescript compile src/extension.affine -o
out/extension.cjs`. Committed so the extension can be installed without
an AffineScript toolchain on PATH. |
| `…/extensions/vscode/package.json` | modified | `main` →
`./src/index.cjs`; `compile` invokes `affinescript compile`; TS/eslint
devDeps dropped; version `0.1.0` → `0.2.0`. |
| `…/extensions/vscode/src/extension.ts` | **deleted** | Replaced. |
| `…/extensions/vscode/tsconfig.json` | **deleted** | No TypeScript to
compile. |

## Acceptance criteria (from
[affinescript#64](hyperpolymath/affinescript#64))

- [x]
[affinescript#35](hyperpolymath/affinescript#35)
has landed.
- [x] Port `rsr-certifier/extensions/vscode/src/extension.ts` →
`.affine`.
- [x] **Activation** verified via `shim.activate` (registers
`ExtensionContext` handle, instantiates wasm with `extraImports`, sets
up status bar, registers commands, starts LSP).
- [x] **Command registration** verified — `rsr.checkCompliance`,
`rsr.initConfig`, `rsr.showReport`, `rsr.generateBadge` registered via
`Vscode::registerCommand` with wasm-table indices `0`–`3` (order matches
handler declarations in `extension.affine`). On-save handler at index
`4`.
- [x] **LSP wiring** verified —
`VscodeLanguageClient::newLanguageClient` + `languageClientStart`, using
`rsr.serverPath` config or
`context.asAbsolutePath("server/rsr-lsp[.exe]")` fallback
(platform-aware via `processPlatform`).
- [x] **Clean disposal** verified — `shim.deactivate` exported;
status-bar item, diagnostic collection, and all command disposables join
`ctx.subscriptions` for host-managed release.

## Feature-parity degradations

The current AffineScript extern-call ABI is synchronous, so two
Thenable-returning vscode APIs cannot be bound:

| API | Used by | Effect on port |
|---|---|---|
| `vscode.window.withProgress(opts, async task)` | the "preferred"
`checkCompliance` path | Removed. All four commands shell out to the
`rsr` CLI in a terminal — the same fallback the original TS extension
already used for the no-LSP case. |
| `LanguageClient.sendRequest(method, params)` | `rsr/getCompliance`
custom request | Removed. The downstream effects (status-bar mutation on
every check result, diagnostic-collection updates) also go. The status
bar shows a static initial value; the diagnostic collection is created
and disposed but not populated. |

`onDidSaveTextDocument`: the handler drops the `TextDocument` argument
at the FFI boundary (wasm-table thunks are zero-arg in this adapter).
The on-save handler is a no-op to avoid spawning a terminal on every
save anywhere; the `rsr.checkOnSave` flag is honoured at the
registration level so the wiring is in place once an async-extern ABI
lands.

`showReport` webview renders a constant placeholder shape rather than
per-check data (the per-check data flowed from the `sendRequest` path
that we cannot bind).

The compile step produces 7 advisory `If without else returns Never`
warnings — these are the same early-return-`if` pattern the affinescript
pilot extension uses, and are cosmetic.

## TS-exemption table

The repo's `.claude/CLAUDE.md` "TypeScript Exemptions" table currently
lists only `avow-protocol/telegram-bot/avow-telegram-bot/**`. The
rsr-certifier `extension.ts` was **not** a per-repo exemption row — it
was covered by the universal-allowlist `*vscode*` directory-segment
pattern in `.github/workflows/rsr-antipattern.yml`. Removing the .ts
file therefore does not require a CLAUDE.md table edit; the file simply
ceases to exist.

## Test plan

- [ ]
[affinescript#102](hyperpolymath/affinescript#102)
is merged and a release is cut (or local toolchain points at that
branch).
- [ ] `npm run compile` succeeds with the AffineScript toolchain on PATH
(or `AFFINESCRIPT_STDLIB` env var pointing at `affinescript/stdlib`).
Confirmed locally: compiles to `out/extension.cjs` cleanly.
- [ ] Install the extension into VS Code (`vsce package` + `code
--install-extension rsr-certified-0.2.0.vsix`).
- [ ] Open a folder with a `.git` directory or `.rsr.toml` — extension
activates; console shows `"RSR-Certified extension is activating..."`;
status bar shows `☆ RSR: SILVER 75%`.
- [ ] `Cmd/Ctrl+Shift+P` → `RSR: Check Compliance` — opens terminal `RSR
Check`, runs `rsr check "<workspace>"`.
- [ ] `RSR: Initialize Configuration` — writes `.rsr.toml` at workspace
root and opens it; info message `"RSR configuration created:
.rsr.toml"`.
- [ ] `RSR: Show Compliance Report` — opens webview panel with the
placeholder report shape.
- [ ] `RSR: Generate Badge` — copies badge markdown to clipboard; info
message confirms.
- [ ] Click the status bar item — fires `rsr.showReport`, panel opens.
- [ ] If `rsr-lsp` is on PATH (or bundled at `server/rsr-lsp[.exe]`),
LSP starts. If absent, the warning `"RSR LSP server not found. Some
features may be limited."` shows; extension continues without LSP.
- [ ] Reload window — `deactivate` runs cleanly; no disposable leaks; CI
antipattern check passes (no `.ts` files).

## Related

- Issue:
[affinescript#64](hyperpolymath/affinescript#64)
- Companion:
[hyperpolymath/affinescript#102](hyperpolymath/affinescript#102)
(binding-surface expansion — blocker)
- Companion:
[hyperpolymath/my-lang#11](hyperpolymath/my-lang#11)
(my-lang side of the same issue — uses only the pre-existing binding
surface)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@hyperpolymath hyperpolymath deleted the port/vscode-extension-to-affine branch May 11, 2026 07:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant