<p>There's a certain lie we all tell ourselves as PHP developers. We say we don't need a step debugger. We have <code>dd()</code>. We have <code>ray()</code>. We have that one friend, <code>var_dump()</code>, who has never let us down (except for all the times it has). We sprinkle these little breadcrumbs through our code, refresh the page, squint at the output, delete the breadcrumbs, and repeat — a ritual so familiar it stops feeling like friction and starts feeling like work.</p><p>But every so often you hit a bug that <code>dd()</code> just can't hold. A value that's correct on line 40 and wrong on line 80, with a dozen branches in between. A collection that's mutated three closures deep. That moment when you'd give anything to just… stop time, look around, and poke at things.</p><p>That's what shipped in e 0.6.7. A real step debugger — breakpoints, a live call stack, variables you can actually inspect — and, true to the spirit of e, it tries hard to feel like it was always part of the editor.</p><h2>Why we built it</h2><p>For a long time, step-debugging was the last big card PhpStorm held. You can love a fast, native editor and still keep a heavyweight IDE around "just for debugging." We didn't want that asterisk next to e anymore. If e is going to be the editor for the rest of us, then setting a breakpoint and pressing a key should be as ordinary as saving a file.</p><p>The trick was doing it the e way: no bloat, nothing to babysit, and — crucially — nothing you have to install just to keep working normally. If you never open the debugger, you should never know it's there.</p><h2>How it works, in one breath</h2><p>Under the hood there's a new little crate, <code>e-dap</code>, a sibling to our existing <code>e-lsp</code>. Where <code>e-lsp</code> speaks the Language Server Protocol, <code>e-dap</code> speaks the Debug Adapter Protocol — the same protocol VS Code uses. That one decision is why this feature punches above its weight: PHP came first, but JavaScript and Rust came along nearly for free.</p><p>For PHP, the chain looks like this:</p><pre><code class="language-text">F5 in e  →  e-dap  →  php-debug adapter  →  Xdebug  →  your app
</code></pre><p>And the runtime half — loading Xdebug, wiring up the ports — is handled by Grove, our local dev environment. Which brings us to the fun part.</p><h2>The 90-second tour</h2><p><strong>1. Turn Xdebug on.</strong> Open Settings (⌘,), go to Laravel, and flip Enable Xdebug. Behind the scenes that runs:</p><pre><code class="language-bash">grove debug on
</code></pre><p>Grove loads Xdebug into its PHP pools in trigger mode — meaning it stays completely dormant, with basically zero overhead, until a request actually asks to be debugged. No permanent tax on your dev server.</p><p>Prefer the terminal? The toggle just mirrors that command, and on startup e reads <code>grove debug status</code> so the switch always tells the truth.</p><p><strong>2. Set a breakpoint.</strong> Click a line and press F9 — or just ⌥-click it. A red dot appears in the margin:</p><pre><code class="language-php">public function store(Request $request)
{
    $data = $request-&gt;validated();
●   $order = Order::create($data);   // ← breakpoint here
    OrderShipped::dispatch($order);
    return redirect()-&gt;route('orders.show', $order);
}
</code></pre><p><strong>3. Press F5.</strong> The Debug panel opens, the adapter spins up over Grove's bundled Node (no nvm, no global installs), and Xdebug quietly starts listening on port 9003.</p><p><strong>4. Trigger the request.</strong> Hit the route from your browser with an Xdebug helper extension, or just add <code>?XDEBUG_TRIGGER=1</code>. Doing it from the CLI instead — an artisan command, a test? One line:</p><pre><code class="language-bash">eval "$(grove debug env)"
php artisan orders:process
</code></pre><p>The moment execution reaches your breakpoint, e jumps to the line, highlights it, and fills the panel with the call stack and every variable in scope. Now you can actually look:</p><pre><code class="language-text">Call Stack
  OrderController-&gt;store   OrderController.php:14
  {closure}                RouteCollection.php:211
  {main}                   index.php:55

Variables
  $data     array(3)
  $order    App\Models\Order
  $request  Illuminate\Http\Request
</code></pre><p>Step through with F10 (over), F11 (into), ⇧F11 (out), or F5 to let it run. Click any frame in the stack to jump straight to that line in your code.</p><h2>The part we're quietly proud of</h2><p>Two small things that took real care:</p><p><strong>It never gets in your way.</strong> Debugging is entirely opt-in. The editor doesn't touch Grove, Xdebug, or any adapter unless you press F5. And when it does, the adapter is launched completely off the UI thread — so even if an adapter is missing, or slow to come up, or you don't have Grove installed at all, the editor never so much as stutters. Worst case, you get a friendly line in the Debug panel telling you what to install. You can even set breakpoints with nothing installed; they'll just be waiting when you're ready.</p><p><strong>It's not only for PHP.</strong> Because <code>e-dap</code> speaks plain DAP over both stdio and TCP, the same panel drives:</p><ul><li><p>PHP via Xdebug (with Grove)</p></li><li><p>JavaScript / TypeScript via vscode-js-debug</p></li><li><p>Rust / C / C++ via codelldb</p></li></ul><p>e picks the right adapter from the file you're in and finds it automatically from your installed VS Code or Cursor extensions. Open a <code>.ts</code> file, press F5, and it Just Works — same breakpoints, same call stack, same muscle memory.</p><h2>The keys, all in one place</h2><p>Key Action F5 Start / continue F9 or ⌥-click Toggle breakpoint F10 Step over F11 Step into ⇧F11 Step out</p><p>Everything's in the command palette too (⌘⇧P → "Debug: …"), and there's a full write-up in the docs under Debugging.</p><h2>Update and try it</h2><p>If you're already running e, the built-in updater will offer 0.6.7 next time you launch. Otherwise, grab it from the releases page.</p><p>Keep your <code>dd()</code>s — they're still great for a quick peek. But the next time you hit one of those bugs, the ones that laugh at breadcrumbs, just click the margin and press F5. Stop time. Look around. It's a good feeling, and now it lives right where you already work.</p><p>Happy debugging. 🐛→🦋</p>