Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
The DAP TCP transport layer was hardcoded to `Ipv4Addr`, so IPv6
addresses like `fd00::a` in a debug config's `connect.host` always
failed with `hostname must be IPv4: invalid IPv4 address syntax`.
Replaced `Ipv4Addr` with `IpAddr` and `SocketAddrV4` with `SocketAddr`
across the `task`, `dap`, `dap_adapters`, and `project` crates. The WASM
extension API still uses `u32` for the host field to avoid a breaking
WIT interface change; IPv4 round-trips through extensions as before.
Fixes#52237
Release Notes:
- Fixed DAP TCP transport rejecting IPv6 addresses when connecting to
remote debug adapters.
---------
Co-authored-by: moktamd <moktamd@users.noreply.github.com>
The Python (debugpy) and Go (delve-shim-dap) debug adapters
unconditionally require network access on every debug session start —
even when the adapter binary is already cached locally. This makes the
debugger completely unusable offline.
Python: fetch_debugpy_whl → maybe_fetch_new_wheel hits pypi; failure
propagates as a fatal error.
Go: install_shim → fetch_latest_adapter_version hits the GitHub API;
failure propagates as a fatal error.
CodeLLDB and JavaScript adapters already handle this correctly.
Fix for Python and Go:
Wrap the network fetch in each adapter with a fallback: if the request
fails and a previously downloaded adapter exists on disk, log a warning
and use the cached version. If no cache exists, the original error
propagates unchanged.
Logs after the fix:
```
2026-03-06T16:31:51+05:30 WARN [dap_adapters::python] Failed to fetch latest debugpy, using cached version: getting latest release
2026-03-06T16:31:51+05:30 INFO [dap::transport] Debug adapter has connected to TCP server 127.0.0.1:45533
```
Limitations:
The debugger must be run at least once with internet connectivity to
populate the cache on disk. This PR does not change that requirement.
Closes#45781
Before you mark this PR as ready for review, make sure that you have:
- [ ] Added a solid test coverage and/or screenshots from doing manual
testing
- No automated tests are included. The existing MockDelegate stubs
http_client() and fs() as unimplemented!(), so testing the fallback path
would require new mock infrastructure. The fix was verified manually by
running the debug build offline with and without cached adapters. The
CodeLLDB adapter's equivalent fallback (lines 357-374) also has no
dedicated test.
- [x] Done a self-review taking into account security and performance
aspects
Release Notes:
- Fixed debugger failing to start when offline if a debug adapter was
previously downloaded.
Here's some backstory:
* on macOS, @cole-miller and I noticed that since roughly Oct 2025, due
to some changes to latest macOS Tahoe, for any spawned child process we
needed to reset Mach exception ports
(https://github.com/zed-industries/zed/issues/36754 +
6e8f2d2ebe)
* the changes in that PR achieve that via `pre_exec` hook on
`std::process::Command` which then abandons `posix_spawn` syscall for
`fork` + `execve` dance on macOS (we tracked it down in Rust's std
implementation)
* as it turns out, `fork` + `execve` is pretty expensive on macOS
(apparently way more so than on other OSes like Linux) and `fork` takes
a process-wide lock on the allocator which is bad
* however, since we wanna reset exception ports on the child, the only
official way supported by Rust's std is to use `pre_exec` hook
* posix_spawn on macOS exposes this tho via a macOS specific extension
to that syscall `posix_spawnattr_setexceptionports_np` but there is no
way to use that via any standard interfaces in `std::process::Command`
* thus, it seemed like a good idea to instead create our own custom
Command wrapper that on non-macOS hosts is a zero-cost wrapper of
`smol::process::Command`, while on macOS we reimplement the minimum to
achieve `smol::process::Command` with `posix_spawn` under-the-hood
Notably, this changeset improves git-blame in very large repos
significantly.
Release Notes:
- Fixed performance spawning child processes on macOS by always forcing
`posix_spawn` no matter what.
---------
Co-authored-by: Cole Miller <cole@zed.dev>
Fixes#48231
## Problem
When debugging Rust programs with CodeLLDB, panic breakpoints ("Rust: on
panic") don't work unless `sourceLanguages: ["rust"]` is explicitly set
in the debug configuration.
Without this setting, CodeLLDB doesn't return the `rust_panic` exception
filter, so the breakpoint shows `locations = 0 (pending)` and never
triggers.
## Root Cause
The Cargo locator correctly adds `sourceLanguages: ["rust"]`
([cargo.rs:97-100](https://github.com/zed-industries/zed/blob/main/crates/project/src/debugger/locators/cargo.rs#L97-L100)),
but other code paths may not:
- VSCode launch.json imports
- Manual debug configurations
## Solution
Auto-detect Rust binaries in `CodeLldbDebugAdapter::get_binary()` by
checking if the program path contains `/target/debug/` or
`/target/release/` (Cargo's standard output directories).
If detected and `sourceLanguages` isn't already set, we automatically
add `["rust"]`.
Release Notes:
- Fixed Rust panic breakpoints not working in debugger when using
CodeLLDB with non-Cargo debug configurations
## Summary
This PR extends the `always_allow` tool permission patterns to work with
Nushell, Elvish, and Rc shells. Previously, these shells were
incorrectly excluded because they don't use `&&`/`||` operators for
command chaining. However, brush-parser can safely parse their command
syntax since they all use `;` for sequential execution.
## Changes
- Add `ShellKind::Nushell`, `ShellKind::Elvish`, and `ShellKind::Rc` to
`supports_posix_chaining()`
- Split `ShellKind::Unknown` into `ShellKind::UnknownWindows` and
`ShellKind::UnknownUnix` to preserve platform-specific fallback behavior
while still denying `always_allow` patterns for unrecognized shells
- Add comprehensive tests for the new shell support
- Clarify documentation about shell compatibility
## Shell Notes
- **Nushell**: Uses `;` for sequential execution. The `and`/`or`
keywords are boolean operators on values, not command chaining.
- **Elvish**: Uses `;` to separate pipelines. Does not have `&&` or `||`
operators. Its `and`/`or` are special commands operating on values.
- **Rc (Plan 9)**: Uses `;` for sequential execution and `|` for piping.
Does not have `&&`/`||` operators.
## Security
Unknown shells still return `false` from `supports_posix_chaining()`, so
`always_allow` patterns are denied for safety when we can't verify the
shell's syntax.
(No release notes because granular tool permissions are still
feature-flagged.)
Release Notes:
- N/A
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
Implements a specialized constructor `LanguageName::new_static` for
`&'static str` which reduces allocations.
`LanguageName::new` always backs the underlying `SharedString` with an
owned `Arc<str>` even when a `&'static str` is passed. This makes us
allocate each time we create a new `LanguageName` no matter what.
Creating a specialized constructor for `&'static str` allows us to
essentially construct them for free.
Additional change:
Encourages using explicit constructors to avoid needless allocations.
Currently there were no instances of this trait being called where the
lifetime was not `'static` saving another 48 locations of allocation.
```rust
impl<'a> From<&'a str> for LanguageName {
fn from(str: &'a str) -> Self {
Self(SharedString::new(str))
}
}
// to
impl From<&'static str> for LanguageName {
fn from(str: &'static str) -> Self {
Self(SharedString::new_static(str))
}
}
```
Release Notes:
- N/A
Release Notes:
- Fixed the behavior of the attach to process feature for the Golang
debugger.
**Step to reprorduce:**
1. Build and run golang binary
2. In Zed open debugger
3. Go to Attach tab
4. Select the binary you just built from the list
**Actual result:**
```
Tried to launch debugger with: {
"request": "attach",
"mode": "debug",
"processId": 74702,
"stopOnEntry": false,
"cwd": "/path/to/the/current/working/directory"
}
error: Failed to attach: invalid debug configuration - unsupported 'mode' attribute "debug"
```
**Expected result:** The debugger can attach to the process.
According to the [Delve
documentation](https://github.com/go-delve/delve/tree/master/Documentation/api/dap#launch-and-attach-configurations)
`attach` request supports `local` and `remote` values only
Closes#42727
Unfortunately we can only support IPv4 addresses right now because
`TcpArguments` only supports an IPv4 address. I'm not sure how difficult
it would be to lift this limitation.
Release Notes:
- Fixed `connect.host` setting being ignored by debugpy
Co-authored-by: Cole Miller <cole@zed.dev>
I really need this for embedded development in IDF and Zephyr, where the
chip vendors sometimes provide there own specialized version of the
tools, and I need to direct zed to use these.
The current GDB adapter only supports the gdb it find in the normal
search path, and it also seems like we where not able to transfer gdb
specific startup arguments (only `-i=dap` is set) .
In order to fix this I (semi wipe using GPT-4.1) expanded the GDB
adapter with 2 new config options :
* **gdb_path** holds a full path to the gdb executable, for now only
full path or relative to cwd
* **gdb_args** an array holding additional arguments given to gdb on
startup
It seemed to me, like the `env` config did not transferred to gdb, so
this is added to.
I have tested this locally, and it seems to work not only compile :-)
Release Notes:
debugger: Adds gdb_path and gdb_args to gdb debug adapter options
debugger: Fix bug where gdb debug sessions wouldn't inherit the shell
environment from Zed
---------
Co-authored-by: Anthony <anthony@zed.dev>
Fixes the Debugpy toolchain detection bug in #40324
When detecting what toolchain (venv) to use in the Debugpy configuration
stage, we used to only base it off of the current working directory
argument passed to the config. This is wrong behavior for cases like
mono repos, where the correct virtual environment to use is nested in
another folder.
This PR fixes this issue by adding the program and module fields as
fallbacks to check for virtual environments. We also added support for
program/module relative paths as well when cwd is not None.
Release Notes:
- debugger: Improve mono repo virtual environment detection with Debugpy
---------
Co-authored-by: Remco Smits <djsmits12@gmail.com>
Closes#38345, #34882, #33280
Debugpy has four distinct configuration scenarios, which are:
1. launch
2. attach with process id
3. attach with listen
4. attach with connect
Spawning Debugpy directly works with the first three scenarios but not
with "attach with connect". Which requires host/port arguments being
passed in both with an attach request and when starting up Debugpy. This
PR passes in the right arguments when spawning Debugpy in an attach with
connect scenario, thus fixing the bug.
The VsCode extension comment that explains this:
98f5b93ee4/src/extension/debugger/adapter/factory.ts (L43-L51)
Release Notes:
- debugger: Fix Python attach-based sessions not working with `connect`
or `port` arguments
Using `shlex` unconditionally is dangerous as it assumes the underlying
shell is POSIX which is not the case for PowerShell, CMD, or Nushell.
Therefore, whenever we want to quote the args we should utilise our
helper `util:🐚:ShellKind::try_quote` which takes into account
which shell is being used to actually exec/spawn the invocation.
Release Notes:
- N/A
---------
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
We've been considering removing workspace-hack for a couple reasons:
- Lukas ran into a situation where its build script seemed to be causing
spurious rebuilds. This seems more likely to be a cargo bug than an
issue with workspace-hack itself (given that it has an empty build
script), but we don't necessarily want to take the time to hunt that
down right now.
- Marshall mentioned hakari interacts poorly with automated crate
updates (in our case provided by rennovate) because you'd need to have
`cargo hakari generate && cargo hakari manage-deps` after their changes
and we prefer to not have actions that make commits.
Currently removing workspace-hack causes our workspace to grow from
~1700 to ~2000 crates being built (depending on platform), which is
mainly a problem when you're building the whole workspace or running
tests across the the normal and remote binaries (which is where
feature-unification nets us the most sharing). It doesn't impact
incremental times noticeably when you're just iterating on `-p zed`, and
we'll hopefully get these savings back in the future when
rust-lang/cargo#14774 (which re-implements the functionality of hakari)
is finished.
Release Notes:
- N/A
It is now possible to configure logging level of CodeLLDB adapter via
envs specified in project settings like so:
```
{
"dap": {
"CodeLLDB": {
"envs": {
"RUST_LOG": "debug"
}
}
}
}
```
Release Notes:
- N/A
Fixes#39998
Debugpy and pylsp are installed in a Zed-global venv with pip. We need a
Python interpreter to create this venv when it doesn't exist and one of
these tools needs to be installed, and sometimes we attempt to use
`python3` from `$PATH`. This can cause issues on Windows, where out of
the box `python3` is a sort of shim that opens the Microsoft Store app.
This PR changes the debugpy installation path to create the Zed-global
venv using the Python interpreter from a venv in the project, and only
use python3 from `$PATH` if that fails. That matches how pylsp
installation already works. It also tightens up how we search for a
global Python installation by doing a basic sanity check (`python3 -c
'print(1 + 2)`) before accepting it, which should catch the Windows
shim.
Release Notes:
- windows: improved the behavior of Zed in situations where no global
Python installation exists.
This PR adds support for browser debugging in SSH and WSL projects. We
use the vscode-js-debug-companion extension, repackaged as a standalone
CLI (https://github.com/zed-industries/js-debug-companion-cli).
Closes#38878
Release Notes:
- debugger: Browser debugging is now supported in SSH and WSL projects.
---------
Co-authored-by: Nia <nia@zed.dev>
Closes https://github.com/zed-industries/zed/issues/38690Closes#37353
### Background
On Windows, paths are normally separated by `\`, unlike mac and linux
where they are separated by `/`. When editing code in a project that
uses a different path style than your local system (e.g. remoting from
Windows to Linux, using WSL, and collaboration between windows and unix
users), the correct separator for a path may differ from the "native"
separator.
Previously, to work around this, Zed converted paths' separators in
numerous places. This was applied to both absolute and relative paths,
leading to incorrect conversions in some cases.
### Solution
Many code paths in Zed use paths that are *relative* to either a
worktree root or a git repository. This PR introduces a dedicated type
for these paths called `RelPath`, which stores the path in the same way
regardless of host platform, and offers `Path`-like manipulation APIs.
RelPath supports *displaying* the path using either separator, so that
we can display paths in a style that is determined at runtime based on
the current project.
The representation of absolute paths is left untouched, for now.
Absolute paths are different from relative paths because (except in
contexts where we know that the path refers to the local filesystem)
they should generally be treated as opaque strings. Currently we use a
mix of types for these paths (std::path::Path, String, SanitizedPath).
Release Notes:
- N/A
---------
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Peter Tripp <petertripp@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: Lukas Wirth <me@lukaswirth.dev>
This PR adds a toggleable filter to the stack frame list that filters
out entries that don't exist within a user's project (visible work
trees). This works by keeping a vector of entry indices that exist
within a user's project and updates the list state based on these
entries when filtering the list.
I went with this approach so the stack frame list wouldn't have to
rebuild itself whenever the filter is toggled and it could persist its
state across toggles (uncollapsing a collapse list). It was also easier
to keep track of selected entries on toggle using the vector as well.
### Preview
https://github.com/user-attachments/assets/d86c7485-c885-4bbb-bebb-2f6385674925
Release Notes:
- debugger: Add option to only show stack frames from user's project in
stack frame list
Another stab at #35388
cc @Sansui233
Closes#35388
Release Notes:
- debugger: Fixed Python debug sessions failing to launch due to a
missing debugpy installation.
Closes#35388
Release Notes:
- debugger: Fixed Python debug sessions failing to launch due to a
missing debugpy installation. Debugpy is now installed into user's venv
if there's one available.
@cole-miller found a root cause of our struggles with attach scenarios;
we did not fetch .so files necessary for attaching to work,
as we were downloading DebugPy source tarballs from GitHub.
This PR does away with it by setting up a virtualenv instead that has
debugpy installed.
Closes#34660Closes#34575
Release Notes:
- debugger: Fixed attaching with DebugPy. DebugPy is now installed
automatically from pip (instead of GitHub), unless it is present in
active virtual environment. Additionally this should resolve any startup
issues with missing .so on Linux.
Previously we would append `js-debug/src/dapDebugServer.js` to the value
of the `dap.JavaScript.binary` setting and `src/debugpy/adapter` to the
value of the `dap.Debugpy.binary` setting, which isn't particularly
intuitive. This PR fixes that.
Release Notes:
- debugger: Made the semantics of the `dap.$ADAPTER.binary` setting more
intuitive for the `JavaScript` and `Debugpy` adapters. In the new
semantics, this should be the path to `dapDebugServer.js` for
`JavaScript` and the path to the `src/debugpy/adapter` directory for
`Debugpy`.
---------
Co-authored-by: Remco Smits <djsmits12@gmail.com>
This PR updates the debugger panel's session list to be more useful in
some cases that are commonly hit when using the JavaScript adapter. We
make two adjustments, which only apply to JavaScript sessions:
- For a child session that's the only child of a root session, we
collapse it with its parent. This imitates what VS Code does in the
"call stack" view for JavaScript sessions.
- When a session has exactly one thread, we label the session with that
thread's name, instead of the session label provided by the DAP. VS Code
also makes this adjustment, which surfaces more useful information when
working with browser sessions.
Closes#33072
Release Notes:
- debugger: Improved the appearance of JavaScript sessions in the debug
panel's session list.
---------
Co-authored-by: Julia <julia@zed.dev>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
This commit removes the PHP debug adapter in favor of a new version
(0.3.0) of PHP extension.
The name of a debug adapter has been changed from "PHP" to "Xdebug",
which makes this a breaking change in user-configured scenarios
Release Notes:
- debugger: PHP debug adapter is no longer shipped in core Zed editor;
it is now available in PHP extension (starting with version 0.3.0). The
adapter has been renamed from `PHP` to `Xdebug`, which might break your
user-defined debug scenarios.
There were a couple of things preventing this from working:
- our hack to stop the node REPL from appearing broke in recent versions
of the JS DAP that started passing `--experimental-network-inspection`
by default
- we had lost the ability to create a debug terminal without specifying
a program
This PR fixes those issues. We also fixed environment variables from the
**runInTerminal** request not getting passed to the spawned program.
Release Notes:
- Debugger: Fix RunInTerminal not working for JavaScript debugger.
---------
Co-authored-by: Cole Miller <cole@zed.dev>
`program` isn't required, and in fact our built-in `JavaScript debug
terminal` configuration doesn't have it.
Also add `node-terminal` to the list of allowed types.
Co-authored-by: Michael <michael@zed.dev>
Release Notes:
- N/A
Just like vscode-js-debug, debugpy uses the `name` key in
StartDebuggingRequestArguments for this:
0d65353cc6/src/debugpy/adapter/clients.py (L753)
Release Notes:
- debugger: Made the names of Python subprocesses in the session list
more helpful.
Related to #33072
We use the JS adapter's suggested names for child sessions, but
sometimes it sends us `""`, so don't use that one.
Release Notes:
- debugger: Fixed nameless child sessions appearing with the JavaScript
adapter.
- **debugger: Update exception breakpoints list on capability update**
- **Do not prefill codelldb sourcelanguages by default**
Release Notes:
- debugger: CodeLLDB no longer enables pretty-printers for Rust by
default. This fixes pretty-printers for C++. This is a breaking change
for user-defined debug scenarios from debug.json; in order to enable
Rust pretty printing when using CodeLLDB, add `"sourceLanguages":
["rust"]` to your debug configuration. This change does not affect
scenarios automatically inferred by Zed.
---------
Co-authored-by: Anthony Eid <anthony@zed.dev>