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
221 changes: 100 additions & 121 deletions editor-extensions/vscode/README.md
Original file line number Diff line number Diff line change
@@ -1,144 +1,93 @@
# ChibiRuby Debugger (VSCode)

DAP-based debug adapter for [ChibiRuby](https://github.com/hadashiA/ChibiRuby). Pause at
`binding.irb`, inspect locals, and evaluate arbitrary Ruby in the Debug Console.
DAP-based debug adapter for [ChibiRuby](https://github.com/hadashiA/ChibiRuby).
Set breakpoints in `.rb` files, inspect locals, and evaluate arbitrary Ruby in
the Debug Console.

## Phase 1 capabilities
## Install

- Suspend at `binding.irb`
- Evaluate Ruby expressions in the suspended binding (Debug Console / Watch / Hover)
- View `self` and locals in the Variables pane
- Continue / terminate

Not yet supported (Phase 2): line breakpoints, step over/into/out, source-map line
display in the call stack. The stack frame currently shows line 1 as a placeholder.

---

## Starting a debug session without launch.json

For ad-hoc attach against a running host, two zero-config paths are available:

- **Command Palette**: `Cmd+Shift+P` → **"ChibiRuby: Attach to running host"** → type a port
(default 4711) → Enter. The session starts immediately.
- **Run-and-Debug picker**: with no `.vscode/launch.json` present, opening the Run-and-Debug
panel offers **"ChibiRuby: Attach to embedded host (4711)"** via the dynamic-configuration
list. One click starts the session.
[![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/v/hadashiA.chibiruby-debugger?label=Marketplace)](https://marketplace.visualstudio.com/items?itemName=hadashiA.chibiruby-debugger)

Both routes assume `MRubyDapTcpServer.Listen(...)` is already running on the chosen port
in your host process. If you need a non-default host/port or want to switch frequently,
write a `launch.json` (see below).

## Two modes

### Attach mode (typical for embedded hosts)

Your C# / Unity host has its own startup and embeds ChibiRuby for scripting. It exposes a
DAP listener via `MRubyDapTcpServer.Listen(...)`. You launch the host first; VSCode
attaches over TCP.

`.vscode/launch.json`:

```json
{
"type": "chibiruby",
"request": "attach",
"name": "ChibiRuby: Attach",
"host": "127.0.0.1",
"port": 4711
}
```
code --install-extension hadashiA.chibiruby-debugger
```

See `sandbox/SampleDebuggerEmbedded/` for a complete working host.
Or search **"ChibiRuby Debugger"** in the Extensions panel.

### Launch mode (single .rb file)
## Capabilities

VSCode spawns `mruby-debug` as a child process; the adapter loads & runs the script
itself. Useful for one-off scripts without writing host code.
- **Line breakpoints** in `.rb` files — click the gutter or press **F9**
- Evaluate Ruby expressions in the suspended binding (Debug Console / Watch / Hover)
- View `self` and locals in the Variables pane
- Continue / terminate
- `binding.irb` as an inline pause (also supported, useful when you can't easily
set a gutter breakpoint — e.g. generated code)

```json
{
"type": "chibiruby",
"request": "launch",
"name": "ChibiRuby: Debug current file",
"program": "${file}"
}
```
Not yet supported: launch mode (run a `.rb` file from VSCode), step
over/into/out.

---

## Setup (local development)
## Quick start (attach to an embedded host)

### 1. Build the DAP adapter (`mruby-debug`)
The extension supports **attach mode** only. Your C# / Unity host embeds
ChibiRuby and opens a TCP listener via `new MRubyDapServer(...).StartAsync()`;
VSCode connects over TCP, sends your breakpoints, and execution halts when a
breakpoint hits (or at any `binding.irb` call).

From the repo root:
See [`sandbox/SampleDebuggerEmbedded/`](../../sandbox/SampleDebuggerEmbedded/)
for a complete working host you can run with `dotnet run`.

```sh
dotnet build -c Release src/ChibiRuby.Debugger.Cli/ChibiRuby.Debugger.Cli.csproj
```

You have two ways to make the adapter discoverable to VSCode:
### Zero-config attach

#### Option A — `dotnet tool` install (clean, but requires a re-install on each rebuild)
Two paths skip the `launch.json` boilerplate:

```sh
cd src/ChibiRuby.Debugger.Cli
dotnet pack -c Release
dotnet tool install -g --add-source ./nupkg ChibiRuby.Debugger.Cli
# `mruby-debug` is now on your PATH (~/.dotnet/tools/mruby-debug)
```
- **Command Palette**: `Cmd+Shift+P` → **"ChibiRuby: Attach to running host"** →
type a port (default 4711) → Enter. The session starts immediately.
- **Run-and-Debug picker**: with no `.vscode/launch.json` present, opening the
Run-and-Debug panel offers **"ChibiRuby: Attach to embedded host (4711)"** via
the dynamic-configuration list. One click starts the session.

The default `adapterCommand: "mruby-debug"` in the extension will resolve via PATH.
Both routes assume `MRubyDapServer` is already listening on the chosen port. If
you need a non-default host/port or want to switch frequently, write a
`launch.json`.

#### Option B — point the extension directly at the build output (fast iteration)
### With launch.json

In your `.vscode/launch.json`:
`.vscode/launch.json`:

```json
{
"type": "chibiruby",
"request": "launch",
"name": "Debug current file (local build)",
"program": "${file}",
"adapterCommand": "dotnet",
"adapterArgs": [
"/abs/path/to/ChibiRuby-Debugger/src/ChibiRuby.Debugger.Cli/bin/Debug/net9.0/ChibiRuby.Debugger.Cli.dll"
"version": "0.2.0",
"configurations": [
{
"type": "chibiruby",
"request": "attach",
"name": "ChibiRuby: Attach",
"host": "127.0.0.1",
"port": 4711
}
]
}
```

No re-install needed; just `dotnet build` after each change.
Start the host, then press **F5** in VSCode.

### 2. Sideload the VSCode extension
### Try it with the sample host

From this directory (`editor-extensions/vscode/`):
1. Open the workspace [`sandbox/SampleDebuggerEmbedded/`](../../sandbox/SampleDebuggerEmbedded/)
in VSCode (it ships with a ready `launch.json`).
2. Open the script (e.g. `scenarios/quest.rb`) and click the gutter on any line
to set a breakpoint.
3. Start the sample host in a terminal:

```sh
# One-time: install @vscode/vsce
npm install -g @vscode/vsce
```sh
dotnet run --project sandbox/SampleDebuggerEmbedded
```

# Package the extension into a .vsix
vsce package
# -> chibiruby-debugger-0.1.0.vsix
4. Press **F5** in VSCode to attach. Execution halts at your breakpoint.

# Install into your VSCode
code --install-extension chibiruby-debugger-0.1.0.vsix
```

Alternatively, **Extension Development Host** for live iteration on the extension itself:

```sh
code editor-extensions/vscode
# In the opened window, press F5 — a second VSCode window opens with the
# extension loaded. Open a .rb file there and start debugging.
```

### 3. Try it

Open `editor-extensions/vscode/examples/sample.rb` in VSCode, press **F5**, choose
the **ChibiRuby: Debug current file** configuration when prompted.

Execution should halt at the `binding.irb` line. In the **Debug Console**, type:
In the **Debug Console**, try evaluating expressions against the current binding:

```
> greeting.upcase
Expand All @@ -149,22 +98,18 @@ Execution should halt at the `binding.irb` line. In the **Debug Console**, type:
"Object"
```

Hit **Continue** (F5) to finish the script.
Hit **Continue** (F5) to resume.

---

## launch.json reference

```json
{
"type": "chibiruby",
"request": "launch",
"name": "ChibiRuby",
"program": "${file}", // Required. .rb file to execute.
"adapterCommand": "mruby-debug", // Optional. Executable that speaks DAP on stdio.
"adapterArgs": [] // Optional. Args prepended to the adapter command.
}
```
| Field | Required | Default | Notes |
|---------|----------|---------------|----------------------------------------------------------------|
| `type` | yes | — | Must be `"chibiruby"`. |
| `request` | yes | — | Must be `"attach"` (launch mode not yet supported). |
| `host` | no | `127.0.0.1` | Hostname / IP `MRubyDapServer` is bound to. |
| `port` | no | `4711` | TCP port `MRubyDapServer` is listening on. |

### Variables surface

Expand All @@ -177,5 +122,39 @@ The Debug Console / Watch pane can use any of:
| `self.foo`, `self.class` | Self-bound calls work because eval'd code runs via `obj.instance_eval(&proc)`. |
| `raise "x"` | The error is reported in the response; the host program is unaffected. |

A Phase 2 release will hook the compiler's `Upper` context so bare identifiers resolve
to outer locals natively.
A future release will hook the compiler's `Upper` context so bare identifiers
resolve to outer locals natively.

---

## Contributing — dev install

If you're hacking on the extension itself:

### Extension Development Host (live iteration)

```sh
code editor-extensions/vscode
# Press F5 in the opened window — a second VSCode window starts with the
# extension loaded. Open a .rb file there and start debugging.
```

### Package & sideload a .vsix

```sh
# One-time:
npm install -g @vscode/vsce

cd editor-extensions/vscode
vsce package
# -> chibiruby-debugger-<version>.vsix

code --install-extension chibiruby-debugger-*.vsix
```

### Publish to Marketplace

Releases are cut by the [`release` workflow](../../.github/workflows/release.yaml),
which packages and publishes to Marketplace when run with `dry-run: false`.
Requires the `VSCE_PAT` repository secret (Azure DevOps PAT, Marketplace →
Manage scope).
11 changes: 6 additions & 5 deletions editor-extensions/zed/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ The extension itself ships no binary — it only tells Zed:
That's the entirety of it. The actual debug server lives in your Unity (or other
.NET) host process via `new MRubyDapServer(...).StartAsync()`.

## Dev install (current distribution)
## Dev install

The extension is not yet on the Zed extension registry. To use it, install it
locally as a dev extension:
The extension is in the process of being submitted to the
[Zed extension registry](https://github.com/zed-industries/extensions). Until it
lands there, install it locally as a dev extension:

1. **Prerequisites** (one-time):
- [Rust](https://rustup.rs) toolchain
Expand Down Expand Up @@ -72,7 +73,7 @@ locally as a dev extension:

## Roadmap

- Submit to [zed-industries/extensions](https://github.com/zed-industries/extensions)
once the API surface stabilizes — then no Rust toolchain / dev install dance.
- Land on the [Zed extension registry](https://github.com/zed-industries/extensions)
so the Rust toolchain / dev-install dance is no longer required.
- Add scenario auto-creation (`dap_config_to_scenario`) so users without a
`debug.json` can still start a session via the Zed UI.
39 changes: 39 additions & 0 deletions editor-extensions/zed/debug_adapter_schemas/chibiruby.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "ChibiRuby Debug Adapter Configuration",
"description": "Schema for `.zed/debug.json` entries with adapter \"chibiruby\". Connects over TCP to MRubyDapServer running inside a host process (Unity or any .NET app).",
"type": "object",
"properties": {
"request": {
"type": "string",
"description": "DAP session kind. ChibiRuby only supports `attach` (the MRubyDapServer is embedded in the host and we connect to it). `launch` is forwarded but not yet honored.",
"enum": ["attach", "launch"],
"default": "attach"
},
"tcp_connection": {
"type": "object",
"description": "Where to reach the embedded MRubyDapServer.",
"properties": {
"host": {
"type": "string",
"description": "Hostname or IP the server is bound to. Use a LAN IP to attach from another machine (e.g. iPhone on the same Wi-Fi).",
"default": "127.0.0.1"
},
"port": {
"type": "integer",
"description": "TCP port the server is listening on. Must match the port passed to `new MRubyDapServer(..., port: ...)` on the host.",
"default": 4711,
"minimum": 1,
"maximum": 65535
},
"timeout": {
"type": "integer",
"description": "Connect timeout in milliseconds. Falls back to Zed's default if omitted.",
"minimum": 0
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}
3 changes: 1 addition & 2 deletions src/ChibiRuby.Unity/Assets/SampleBehaviour.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ void Start()
// In a real game build (no asset on disk) you'd want a synthetic but stable
// path like "ruby/sample.rb" instead.
var scriptPath = $"{Application.dataPath}/ruby/sample.rb";
// using var compilation = compiler.CompileFile(scriptPath);
using var compilation = compiler.Compile(File.ReadAllBytes(scriptPath));
using var compilation = compiler.CompileFile(scriptPath);

var fiber = mrb.ParseBytecodeAsFiber(compilation.AsBytecode());
fiber.Resume(Array.Empty<MRubyValue>());
Expand Down
13 changes: 6 additions & 7 deletions src/ChibiRuby.Unity/Assets/packages.config
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
¼?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ChibiRuby" version="0.108.0" manuallyInstalled="true" />
<package id="ChibiRuby.Compiler" version="0.108.0" manuallyInstalled="true" />
<package id="ChibiRuby.Debugger" version="0.108.0" manuallyInstalled="true" />
<package id="ChibiRuby.Debugger.Dap" version="0.108.0" manuallyInstalled="true" />
<package id="ChibiRuby.Serializer" version="0.107.0" manuallyInstalled="true" />
<packages>
<package id="ChibiRuby" version="1.2.0" manuallyInstalled="true" />
<package id="ChibiRuby.Compiler" version="1.2.0" manuallyInstalled="true" />
<package id="ChibiRuby.Debugger" version="1.2.0" manuallyInstalled="true" />
<package id="ChibiRuby.Debugger.Dap" version="1.2.0" manuallyInstalled="true" />
<package id="ChibiRuby.Serializer" version="1.2.0" manuallyInstalled="true" />
<package id="System.IO.Pipelines" version="9.0.0" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" />
<package id="Utf8StringInterpolation" version="1.3.2" manuallyInstalled="true" />
Expand Down
8 changes: 8 additions & 0 deletions src/ChibiRuby.Unity/Assets/ruby/.zed/debug.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"label": "Attach to ChibiRuby",
"adapter": "chibiruby",
"request": "attach",
"tcp_connection": { "host": "127.0.0.1", "port": 4711 },
},
]
Loading