docs/src/debugger.md
Zed uses the Debug Adapter Protocol (DAP) to provide debugging functionality across multiple programming languages. DAP is a standardized protocol that defines how debuggers, editors, and IDEs communicate with each other. It allows Zed to support various debuggers without needing to implement language-specific debugging logic. Zed implements the client side of the protocol, and various debug adapters implement the server side.
This protocol enables features like setting breakpoints, stepping through code, inspecting variables, and more, in a consistent manner across different programming languages and runtime environments.
To debug code written in a specific language, Zed needs to find a debug adapter for that language. Some debug adapters are provided by Zed without additional setup, and some are provided by language extensions. The following languages currently have debug adapters available:
<!-- keep this sorted -->If your language isn't listed, you can contribute by adding a debug adapter for it. Check out our debugger extensions documentation for more information.
Follow those links for language- and adapter-specific information and examples, or read on for more about Zed's general debugging features that apply to all adapters.
For most languages, the fastest way to get started is to run {#action debugger::Start} ({#kb debugger::Start}). This opens the new process modal, which shows you a contextual list of preconfigured debug tasks for the current project. Debug tasks are created from tests, entry points (like a main function), and from other sources — consult the documentation for your language for full information about what's supported.
You can open the same modal by clicking the "plus" button at the top right of the debug panel.
For languages that don't provide preconfigured debug tasks (this includes C, C++, and some extension-supported languages), you can define debug configurations in the .zed/debug.json file in your project root. This file should be an array of configuration objects:
[
{
"adapter": "CodeLLDB",
"label": "First configuration"
// ...
},
{
"adapter": "Debugpy",
"label": "Second configuration"
// ...
}
]
Check the documentation for your language for example configurations covering typical use-cases. Once you've added configurations to .zed/debug.json, they'll appear in the list in the new process modal.
Zed will also load debug configurations from .vscode/launch.json, and show them in the new process modal if no configurations are found in .zed/debug.json.
If you run the same launch profiles across multiple projects, you can store them once in your user configuration. Invoke {#action zed::OpenDebugTasks} from the command palette to open the global debug.json file; Zed creates it next to your user settings.json and keeps it in sync with the debugger UI. The file lives at:
~/Library/Application Support/Zed/debug.json$XDG_CONFIG_HOME/zed/debug.json (falls back to ~/.config/zed/debug.json)%APPDATA%\Zed\debug.jsonPopulate this file with the same array of objects you would place in .zed/debug.json. Any scenarios defined there are merged into every workspace, so your favorite launch presets appear automatically in the "New Debug Session" dialog.
Zed debugger offers two ways to debug your program; you can either launch a new instance of your program or attach to an existing process. Which one you choose depends on what you are trying to achieve.
When launching a new instance, Zed (and the underlying debug adapter) can often do a better job at picking up the debug information compared to attaching to an existing process, since it controls the lifetime of a whole program. Running unit tests or a debug build of your application is a good use case for launching.
Compared to launching, attaching to an existing process might seem inferior, but that's far from truth; there are cases where you cannot afford to restart your program, because for example, the bug is not reproducible outside of a production environment or some other circumstances.
Zed requires the adapter and label fields for all debug tasks. In addition, Zed will use the build field to run any necessary setup steps before the debugger starts (see below), and can accept a tcp_connection field to connect to an existing process.
All other fields are provided by the debug adapter and can contain task variables. Most adapters support request, program, and cwd:
[
{
// The label for the debug configuration and used to identify the debug session inside the debug panel & new process modal
"label": "Example Start debugger config",
// The debug adapter that Zed should use to debug the program
"adapter": "Example adapter name",
// Request:
// - launch: Zed will launch the program if specified, or show a debug terminal with the right configuration
// - attach: Zed will attach to a running program to debug it, or when the process_id is not specified, will show a process picker (only supported for node currently)
"request": "launch",
// The program to debug. This field supports path resolution with ~ or . symbols.
"program": "path_to_program",
// cwd: defaults to the current working directory of your project ($ZED_WORKTREE_ROOT)
"cwd": "$ZED_WORKTREE_ROOT"
}
]
Check your debug adapter's documentation for more information on the fields it supports.
Zed allows embedding a Zed task in the build field that is run before the debugger starts. This is useful for setting up the environment or running any necessary setup steps before the debugger starts.
[
{
"label": "Build Binary",
"adapter": "CodeLLDB",
"program": "path_to_program",
"request": "launch",
"build": {
"command": "make",
"args": ["build", "-j8"]
}
}
]
Build tasks can also refer to the existing tasks by unsubstituted label:
[
{
"label": "Build Binary",
"adapter": "CodeLLDB",
"program": "path_to_program",
"request": "launch",
"build": "my build task" // Or "my build task for $ZED_FILE"
}
]
Given a Zed task, Zed can automatically create a scenario for you. Automatic scenario creation also powers our scenario creation from gutter. Automatic scenario creation is currently supported for Rust, Go, Python, JavaScript, and TypeScript.
To set a breakpoint, simply click next to the line number in the editor gutter. Breakpoints can be tweaked depending on your needs; to access additional options of a given breakpoint, right-click on the breakpoint icon in the gutter and select the desired option. At present, you can:
Some debug adapters (e.g. CodeLLDB and JavaScript) will also verify whether your breakpoints can be hit; breakpoints that cannot be hit are surfaced more prominently in the UI.
All breakpoints enabled for a given project are also listed in "Breakpoints" item in your debugging session UI. From "Breakpoints" item in your UI you can also manage exception breakpoints. The debug adapter will then stop whenever an exception of a given kind occurs. Which exception types are supported depends on the debug adapter.
When debugging with multiple split panes open, Zed shows the active debug line in one pane and preserves your layout in others. If you have the same file open in multiple panes, the debugger picks a pane where the file is already the active tab—it won't switch tabs in panes where the file is inactive.
Once the debugger picks a pane, it continues using that pane for subsequent breakpoints during the session. If you drag the tab with the active debug line to a different split, the debugger tracks the move and uses the new pane.
This ensures the debugger doesn't disrupt your workflow when stepping through code across different files.
The settings for the debugger are grouped under the debugger key in settings.json:
dock: Determines the position of the debug panel in the UI.stepping_granularity: Determines the stepping granularity.save_breakpoints: Whether the breakpoints should be reused across Zed sessions.button: Whether to show the debug button in the status bar.timeout: Time in milliseconds until timeout error when connecting to a TCP debug adapter.log_dap_communications: Whether to log messages between active debug adapters and Zed.format_dap_log_messages: Whether to format DAP messages when adding them to the debug adapter logger.bottomOptions
left - The debug panel will be docked to the left side of the UI.right - The debug panel will be docked to the right side of the UI.bottom - The debug panel will be docked to the bottom of the UI."debugger": {
"dock": "bottom"
},
linedebugger.stepping_granularityOptions
for(int i = 0; i < 10; i++) could be considered to have 3 statements int i = 0, i < 10, and i++.{
"debugger": {
"stepping_granularity": "statement"
}
}
{
"debugger": {
"stepping_granularity": "line"
}
}
{
"debugger": {
"stepping_granularity": "instruction"
}
}
truedebugger.save_breakpointsOptions
boolean values
{
"debugger": {
"save_breakpoints": true
}
}
truedebugger.buttonOptions
boolean values
{
"debugger": {
"button": true
}
}
2000debugger.timeoutOptions
integer values
{
"debugger": {
"timeout": 3000
}
}
trueinlay_hints.show_value_hintsOptions
{
"inlay_hints": {
"show_value_hints": false
}
}
Inline value hints can also be toggled from the Editor Controls menu in the editor toolbar.
Options
boolean values
{
"debugger": {
"log_dap_communications": true
}
}
Options
boolean values
{
"debugger": {
"format_dap_log_messages": true
}
}
dap.$ADAPTER.binary and dap.$ADAPTER.argsYou can pass binary, args, or both. binary should be a path to a debug adapter (like lldb-dap) not a debugger (like lldb itself). The args setting overrides any arguments that Zed would otherwise pass to the adapter.
{
"dap": {
"CodeLLDB": {
"binary": "/Users/name/bin/lldb-dap",
"args": ["--wait-for-debugger"]
}
}
}
The Debugger supports the following theme options:
debugger.accent: Color used to accent breakpoint & breakpoint-related symbolseditor.debugger_active_line.background: Background color of active debug lineIf you're running into problems with the debugger, please open a GitHub issue, providing as much context as possible. There are also some features you can use to gather more information about the problem: