Cargo.lock 🔗
@@ -15588,6 +15588,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"hex",
+ "log",
"parking_lot",
"pretty_assertions",
"proto",
Anthony Eid created
This PR also shows more completion items when defining a debug config in
a `debug.json` file. Mainly when using a pre build task argument.
### Follow ups
- Add docs for Go, JS, PHP
- Add attach docs
Release Notes:
- debugger beta: Show build task completions when editing a debug.json
configuration with a pre build task
- debugger beta: Add Python and Native Code debug config
[examples](https://zed.dev/docs/debugger)
Cargo.lock | 1
crates/dap_adapters/src/python.rs | 2
crates/task/Cargo.toml | 3
crates/task/src/debug_format.rs | 203 ++++++++++++++++++++++++++++++++
docs/src/debugger.md | 123 +++++++++++++++++--
5 files changed, 310 insertions(+), 22 deletions(-)
@@ -15588,6 +15588,7 @@ dependencies = [
"futures 0.3.31",
"gpui",
"hex",
+ "log",
"parking_lot",
"pretty_assertions",
"proto",
@@ -660,7 +660,7 @@ impl DebugAdapter for PythonDebugAdapter {
}
}
- self.get_installed_binary(delegate, &config, None, None, false)
+ self.get_installed_binary(delegate, &config, None, toolchain, false)
.await
}
}
@@ -20,6 +20,7 @@ collections.workspace = true
futures.workspace = true
gpui.workspace = true
hex.workspace = true
+log.workspace = true
parking_lot.workspace = true
proto.workspace = true
schemars.workspace = true
@@ -29,8 +30,8 @@ serde_json_lenient.workspace = true
sha2.workspace = true
shellexpand.workspace = true
util.workspace = true
-zed_actions.workspace = true
workspace-hack.workspace = true
+zed_actions.workspace = true
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
@@ -1,10 +1,12 @@
use anyhow::{Context as _, Result};
use collections::FxHashMap;
use gpui::SharedString;
+use log as _;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::net::Ipv4Addr;
use std::path::PathBuf;
+use util::debug_panic;
use crate::{TaskTemplate, adapter_schema::AdapterSchemas};
@@ -182,7 +184,7 @@ impl From<AttachRequest> for DebugRequest {
}
}
-#[derive(Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
+#[derive(Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)]
#[serde(untagged)]
pub enum BuildTaskDefinition {
ByName(SharedString),
@@ -194,6 +196,47 @@ pub enum BuildTaskDefinition {
},
}
+impl<'de> Deserialize<'de> for BuildTaskDefinition {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ #[derive(Deserialize)]
+ struct TemplateHelper {
+ #[serde(default)]
+ label: Option<String>,
+ #[serde(flatten)]
+ rest: serde_json::Value,
+ }
+
+ let value = serde_json::Value::deserialize(deserializer)?;
+
+ if let Ok(name) = serde_json::from_value::<SharedString>(value.clone()) {
+ return Ok(BuildTaskDefinition::ByName(name));
+ }
+
+ let helper: TemplateHelper =
+ serde_json::from_value(value).map_err(serde::de::Error::custom)?;
+
+ let mut template_value = helper.rest;
+ if let serde_json::Value::Object(ref mut map) = template_value {
+ map.insert(
+ "label".to_string(),
+ serde_json::to_value(helper.label.unwrap_or_else(|| "debug-build".to_owned()))
+ .map_err(serde::de::Error::custom)?,
+ );
+ }
+
+ let task_template: TaskTemplate =
+ serde_json::from_value(template_value).map_err(serde::de::Error::custom)?;
+
+ Ok(BuildTaskDefinition::Template {
+ task_template,
+ locator_name: None,
+ })
+ }
+}
+
#[derive(Deserialize, Serialize, PartialEq, Eq, Clone, Debug, JsonSchema)]
pub enum Request {
Launch,
@@ -243,9 +286,96 @@ pub struct DebugScenario {
pub struct DebugTaskFile(pub Vec<DebugScenario>);
impl DebugTaskFile {
- /// Generates JSON schema of Tasks JSON template format.
pub fn generate_json_schema(schemas: &AdapterSchemas) -> serde_json_lenient::Value {
- schemas.generate_json_schema().unwrap_or_default()
+ let build_task_schema = schemars::schema_for!(BuildTaskDefinition);
+ let mut build_task_value =
+ serde_json_lenient::to_value(&build_task_schema).unwrap_or_default();
+
+ if let Some(template_object) = build_task_value
+ .get_mut("anyOf")
+ .and_then(|array| array.as_array_mut())
+ .and_then(|array| array.get_mut(1))
+ {
+ if let Some(properties) = template_object
+ .get_mut("properties")
+ .and_then(|value| value.as_object_mut())
+ {
+ properties.remove("label");
+ }
+
+ if let Some(arr) = template_object
+ .get_mut("required")
+ .and_then(|array| array.as_array_mut())
+ {
+ arr.retain(|v| v.as_str() != Some("label"));
+ }
+ } else {
+ debug_panic!("Task Template schema in debug scenario's needs to be updated");
+ }
+
+ let task_definitions = build_task_value
+ .get("definitions")
+ .cloned()
+ .unwrap_or_default();
+
+ let adapter_conditions = schemas
+ .0
+ .iter()
+ .map(|adapter_schema| {
+ let adapter_name = adapter_schema.adapter.to_string();
+ serde_json::json!({
+ "if": {
+ "properties": {
+ "adapter": { "const": adapter_name }
+ }
+ },
+ "then": adapter_schema.schema
+ })
+ })
+ .collect::<Vec<_>>();
+
+ serde_json_lenient::json!({
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Debug Configurations",
+ "description": "Configuration for debug scenarios",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["adapter", "label"],
+ "properties": {
+ "adapter": {
+ "type": "string",
+ "description": "The name of the debug adapter"
+ },
+ "label": {
+ "type": "string",
+ "description": "The name of the debug configuration"
+ },
+ "build": build_task_value,
+ "tcp_connection": {
+ "type": "object",
+ "description": "Optional TCP connection information for connecting to an already running debug adapter",
+ "properties": {
+ "port": {
+ "type": "integer",
+ "description": "The port that the debug adapter is listening on (default: auto-find open port)"
+ },
+ "host": {
+ "type": "string",
+ "pattern": "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$",
+ "description": "The host that the debug adapter is listening to (default: 127.0.0.1)"
+ },
+ "timeout": {
+ "type": "integer",
+ "description": "The max amount of time in milliseconds to connect to a tcp DAP before returning an error (default: 2000ms)"
+ }
+ }
+ }
+ },
+ "allOf": adapter_conditions
+ },
+ "definitions": task_definitions
+ })
}
}
@@ -254,6 +384,32 @@ mod tests {
use crate::DebugScenario;
use serde_json::json;
+ #[test]
+ fn test_just_build_args() {
+ let json = r#"{
+ "label": "Build & debug rust",
+ "adapter": "CodeLLDB",
+ "build": {
+ "command": "rust",
+ "args": ["build"]
+ }
+ }"#;
+
+ let deserialized: DebugScenario = serde_json::from_str(json).unwrap();
+ assert!(deserialized.build.is_some());
+ match deserialized.build.as_ref().unwrap() {
+ crate::BuildTaskDefinition::Template { task_template, .. } => {
+ assert_eq!("debug-build", task_template.label);
+ assert_eq!("rust", task_template.command);
+ assert_eq!(vec!["build"], task_template.args);
+ }
+ _ => panic!("Expected Template variant"),
+ }
+ assert_eq!(json!({}), deserialized.config);
+ assert_eq!("CodeLLDB", deserialized.adapter.as_ref());
+ assert_eq!("Build & debug rust", deserialized.label.as_ref());
+ }
+
#[test]
fn test_empty_scenario_has_none_request() {
let json = r#"{
@@ -307,4 +463,45 @@ mod tests {
assert_eq!("CodeLLDB", deserialized.adapter.as_ref());
assert_eq!("Attach to process", deserialized.label.as_ref());
}
+
+ #[test]
+ fn test_build_task_definition_without_label() {
+ use crate::BuildTaskDefinition;
+
+ let json = r#""my_build_task""#;
+ let deserialized: BuildTaskDefinition = serde_json::from_str(json).unwrap();
+ match deserialized {
+ BuildTaskDefinition::ByName(name) => assert_eq!("my_build_task", name.as_ref()),
+ _ => panic!("Expected ByName variant"),
+ }
+
+ let json = r#"{
+ "command": "cargo",
+ "args": ["build", "--release"]
+ }"#;
+ let deserialized: BuildTaskDefinition = serde_json::from_str(json).unwrap();
+ match deserialized {
+ BuildTaskDefinition::Template { task_template, .. } => {
+ assert_eq!("debug-build", task_template.label);
+ assert_eq!("cargo", task_template.command);
+ assert_eq!(vec!["build", "--release"], task_template.args);
+ }
+ _ => panic!("Expected Template variant"),
+ }
+
+ let json = r#"{
+ "label": "Build Release",
+ "command": "cargo",
+ "args": ["build", "--release"]
+ }"#;
+ let deserialized: BuildTaskDefinition = serde_json::from_str(json).unwrap();
+ match deserialized {
+ BuildTaskDefinition::Template { task_template, .. } => {
+ assert_eq!("Build Release", task_template.label);
+ assert_eq!("cargo", task_template.command);
+ assert_eq!(vec!["build", "--release"], task_template.args);
+ }
+ _ => panic!("Expected Template variant"),
+ }
+ }
}
@@ -28,15 +28,15 @@ These adapters enable Zed to provide a consistent debugging experience across mu
## Getting Started
-For basic debugging you can set up a new configuration by opening the `New Session Modal` either via the `debugger: start` (default: f4) or clicking the plus icon at the top right of the debug panel.
+For basic debugging, you can set up a new configuration by opening the `New Session Modal` either via the `debugger: start` (default: f4) or by clicking the plus icon at the top right of the debug panel.
-For more advanced use cases you can create debug configurations by directly editing the `.zed/debug.json` file in your project root directory.
+For more advanced use cases, you can create debug configurations by directly editing the `.zed/debug.json` file in your project root directory.
-You can then use the `New Session Modal` to select a configuration then start debugging.
+You can then use the `New Session Modal` to select a configuration and start debugging.
### Configuration
-While configuration fields are debug adapter dependent, most adapters support the following fields.
+While configuration fields are debug adapter-dependent, most adapters support the following fields:
```json
[
@@ -58,22 +58,114 @@ While configuration fields are debug adapter dependent, most adapters support th
]
```
-#### Task Variables
+#### Tasks
-All configuration fields support task variables. See [Tasks](./tasks.md)
+All configuration fields support task variables. See [Tasks Variables](./tasks.md#variables)
+
+Zed also allows embedding a task 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.
+
+See an example [here](#build-binary-then-debug)
+
+#### Python Examples
+
+##### Python Active File
+
+```json
+[
+ {
+ "label": "Active File",
+ "adapter": "Debugpy",
+ "program": "$ZED_FILE",
+ "request": "launch"
+ }
+]
+```
+
+##### Flask App
+
+For a common Flask Application with a file structure similar to the following:
+
+- .venv/
+- app/
+ - **init**.py
+ - **main**.py
+ - routes.py
+- templates/
+ - index.html
+- static/
+ - style.css
+- requirements.txt
+
+```json
+[
+ {
+ "label": "Python: Flask",
+ "adapter": "Debugpy",
+ "request": "launch",
+ "module": "app",
+ "cwd": "$ZED_WORKTREE_ROOT",
+ "env": {
+ "FLASK_APP": "app",
+ "FLASK_DEBUG": "1"
+ },
+ "args": [
+ "run",
+ "--reload", // Enables Flask reloader that watches for file changes
+ "--debugger" // Enables Flask debugger
+ ],
+ "autoReload": {
+ "enable": true
+ },
+ "jinja": true,
+ "justMyCode": true
+ }
+]
+```
+
+#### Rust/C++/C
+
+##### Using pre-built binary
+
+```json
+[
+ {
+ "label": "Debug native binary",
+ "program": "$ZED_WORKTREE_ROOT/build/binary",
+ "request": "launch",
+ "adapter": "CodeLLDB" // GDB is available on non arm macs as well as linux
+ }
+]
+```
+
+##### Build binary then debug
+
+```json
+[
+ {
+ "label": "Build & Debug native binary",
+ "build": {
+ "command": "cargo",
+ "args": ["build"]
+ },
+ "program": "$ZED_WORKTREE_ROOT/target/debug/binary",
+ "request": "launch",
+ "adapter": "CodeLLDB" // GDB is available on non arm macs as well as linux
+ }
+]
+```
## Breakpoints
-Zed currently supports these types of breakpoints
+Zed currently supports these types of breakpoints:
- Standard Breakpoints: Stop at the breakpoint when it's hit
- Log Breakpoints: Output a log message instead of stopping at the breakpoint when it's hit
- Conditional Breakpoints: Stop at the breakpoint when it's hit if the condition is met
- Hit Breakpoints: Stop at the breakpoint when it's hit a certain number of times
-Standard breakpoints can be toggled by left clicking on the editor gutter or using the Toggle Breakpoint action. Right clicking on a breakpoint or on a code runner symbol brings up the breakpoint context menu. This has options for toggling breakpoints and editing log breakpoints.
+Standard breakpoints can be toggled by left-clicking on the editor gutter or using the Toggle Breakpoint action. Right-clicking on a breakpoint or on a code runner symbol brings up the breakpoint context menu. This has options for toggling breakpoints and editing log breakpoints.
-Other kinds of breakpoints can be toggled/edited by right clicking on the breakpoint icon in the gutter and selecting the desired option.
+Other kinds of breakpoints can be toggled/edited by right-clicking on the breakpoint icon in the gutter and selecting the desired option.
## Settings
@@ -81,8 +173,8 @@ Other kinds of breakpoints can be toggled/edited by right clicking on the breakp
- `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 in when adding them to debug adapter logger
+- `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.
### Stepping granularity
@@ -163,7 +255,7 @@ Other kinds of breakpoints can be toggled/edited by right clicking on the breakp
### Timeout
- Description: Time in milliseconds until timeout error when connecting to a TCP debug adapter.
-- Default: 2000ms
+- Default: 2000
- Setting: debugger.timeout
**Options**
@@ -198,7 +290,7 @@ Other kinds of breakpoints can be toggled/edited by right clicking on the breakp
### Format Dap Log Messages
-- Description: Whether to format dap messages in when adding them to debug adapter logger. (Used for DAP development)
+- Description: Whether to format DAP messages when adding them to the debug adapter logger. (Used for DAP development)
- Default: false
- Setting: debugger.format_dap_log_messages
@@ -218,8 +310,5 @@ Other kinds of breakpoints can be toggled/edited by right clicking on the breakp
The Debugger supports the following theme options:
- /// Color used to accent some of the debugger's elements
- /// Only accents breakpoint & breakpoint related symbols right now
-
-**debugger.accent**: Color used to accent breakpoint & breakpoint related symbols
+**debugger.accent**: Color used to accent breakpoint & breakpoint-related symbols
**editor.debugger_active_line.background**: Background color of active debug line