1# Debugger Extensions
2
3[Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol) Servers can be exposed as extensions for use in the [debugger](../debugger.md).
4
5## Defining Debugger Extensions
6
7A given extension may provide one or more DAP servers.
8Each DAP server must be registered in the `extension.toml`:
9
10```toml
11[debug_adapters.my-debug-adapter]
12# Optional relative path to the JSON schema for the debug adapter configuration schema. Defaults to `debug_adapter_schemas/$DEBUG_ADAPTER_NAME_ID.json`.
13# Note that while this field is optional, a schema is mandatory.
14schema_path = "relative/path/to/schema.json"
15```
16
17Then, in the Rust code for your extension, implement the `get_dap_binary` method on your extension:
18
19```rust
20impl zed::Extension for MyExtension {
21 fn get_dap_binary(
22 &mut self,
23 adapter_name: String,
24 config: DebugTaskDefinition,
25 user_provided_debug_adapter_path: Option<String>,
26 worktree: &Worktree,
27 ) -> Result<DebugAdapterBinary, String>;
28}
29```
30
31This method should return the command to start up a debug adapter protocol server, along with any arguments or environment variables necessary for it to function.
32
33If you need to download the DAP server from an external source—like GitHub Releases or npm—you can also do that in this function. Make sure to check for updates only periodically, as this function is called whenever a user spawns a new debug session with your debug adapter.
34
35You must also implement `dap_request_kind`. This function is used to determine whether a given debug scenario will _launch_ a new debuggee or _attach_ to an existing one.
36We also use it to determine that a given debug scenario requires running a _locator_.
37
38```rust
39impl zed::Extension for MyExtension {
40 fn dap_request_kind(
41 &mut self,
42 _adapter_name: String,
43 _config: Value,
44 ) -> Result<StartDebuggingRequestArgumentsRequest, String>;
45}
46```
47
48These two functions are sufficient to expose your debug adapter in `debug.json`-based user workflows, but you should strongly consider implementing `dap_config_to_scenario` as well.
49
50```rust
51impl zed::Extension for MyExtension {
52 fn dap_config_to_scenario(
53 &mut self,
54 _adapter_name: DebugConfig,
55 ) -> Result<DebugScenario, String>;
56}
57```
58
59`dap_config_to_scenario` is used when the user spawns a session via new process modal UI. At a high level, it takes a generic debug configuration (that isn't specific to any
60debug adapter) and tries to turn it into a concrete debug scenario for your adapter.
61Put another way, it is supposed to answer the question: "Given a program, a list of arguments, current working directory and environment variables, what would the configuration for spawning this debug adapter look like?".
62
63## Defining Debug Locators
64
65Zed offers an automatic way to create debug scenarios with _debug locators_.
66A locator locates the debug target and figures out how to spawn a debug session for it. Thanks to locators, we can automatically convert existing user tasks (e.g. `cargo run`) and convert them into debug scenarios (e.g. `cargo build` followed by spawning a debugger with `target/debug/my_program` as the program to debug).
67
68> Your extension can define its own debug locators even if it does not expose a debug adapter. We strongly recommend doing so when your extension already exposes language tasks, as it allows users to spawn a debug session without having to manually configure the debug adapter.
69
70Locators can (but don't have to) be agnostic to the debug adapter they are used with. They are simply responsible for locating the debug target and figuring out how to spawn a debug session for it. This allows for a more flexible and extensible debugging experience.
71
72Your extension can define one or more debug locators. Each debug locator must be registered in the `extension.toml`:
73
74```toml
75[debug_locators.my-debug-locator]
76```
77
78Locators have two components.
79First, each locator is ran on each available task to figure out if any of the available locators can provide a debug scenario for a given task. This is done by calling `dap_locator_create_scenario`.
80
81```rust
82impl zed::Extension for MyExtension {
83 fn dap_locator_create_scenario(
84 &mut self,
85 _locator_name: String,
86 _build_task: TaskTemplate,
87 _resolved_label: String,
88 _debug_adapter_name: String,
89 ) -> Option<DebugScenario>;
90}
91```
92
93This function should return `Some` debug scenario when that scenario defines a debugging counterpart to a given user task.
94Note that a `DebugScenario` can include a [build task](../debugger.md#build-tasks). If there is one, we will execute `run_dap_locator` after a build task is finished successfully.
95
96```rust
97impl zed::Extension for MyExtension {
98 fn run_dap_locator(
99 &mut self,
100 _locator_name: String,
101 _build_task: TaskTemplate,
102 ) -> Result<DebugRequest, String>;
103}
104```
105
106`run_dap_locator` is useful in case you cannot determine a build target deterministically. Some build systems may produce artifacts whose names are not known up-front.
107Note however that you do _not_ need to go through a 2-phase resolution; if you can determine the full debug configuration with just `dap_locator_create_scenario`, you can omit `build` property on a returned `DebugScenario`. Please also note that your locator **will be** called with tasks it's unlikely to accept; thus you should take some effort to return `None` early before performing any expensive operations.
108
109## Available Extensions
110
111Check out all the DAP servers that have already been exposed as extensions [on Zed's site](https://zed.dev/extensions?filter=debug-adapters).
112
113We recommend taking a look at their repositories as a way to understand how they are generally created and structured.
114
115## Testing
116
117To test your new Debug Adapter Protocol server extension, you can [install it as a dev extension](./developing-extensions.md#developing-an-extension-locally).