diff --git a/crates/dap_adapters/src/codelldb.rs b/crates/dap_adapters/src/codelldb.rs index 5d14cc8747..5b88db4432 100644 --- a/crates/dap_adapters/src/codelldb.rs +++ b/crates/dap_adapters/src/codelldb.rs @@ -22,17 +22,16 @@ impl CodeLldbDebugAdapter { async fn request_args( &self, delegate: &Arc, - task_definition: &DebugTaskDefinition, + mut configuration: Value, + label: &str, ) -> Result { - // CodeLLDB uses `name` for a terminal label. - let mut configuration = task_definition.config.clone(); - let obj = configuration .as_object_mut() .context("CodeLLDB is not a valid json object")?; + // CodeLLDB uses `name` for a terminal label. obj.entry("name") - .or_insert(Value::String(String::from(task_definition.label.as_ref()))); + .or_insert(Value::String(String::from(label))); obj.entry("cwd") .or_insert(delegate.worktree_root_path().to_string_lossy().into()); @@ -361,17 +360,31 @@ impl DebugAdapter for CodeLldbDebugAdapter { self.path_to_codelldb.set(path.clone()).ok(); command = Some(path); }; - + let mut json_config = config.config.clone(); Ok(DebugAdapterBinary { command: Some(command.unwrap()), cwd: Some(delegate.worktree_root_path().to_path_buf()), arguments: user_args.unwrap_or_else(|| { - vec![ - "--settings".into(), - json!({"sourceLanguages": ["cpp", "rust"]}).to_string(), - ] + if let Some(config) = json_config.as_object_mut() + && let Some(source_languages) = config.get("sourceLanguages").filter(|value| { + value + .as_array() + .map_or(false, |array| array.iter().all(Value::is_string)) + }) + { + let ret = vec![ + "--settings".into(), + json!({"sourceLanguages": source_languages}).to_string(), + ]; + config.remove("sourceLanguages"); + ret + } else { + vec![] + } }), - request_args: self.request_args(delegate, &config).await?, + request_args: self + .request_args(delegate, json_config, &config.label) + .await?, envs: HashMap::default(), connection: None, }) diff --git a/crates/debugger_tools/src/dap_log.rs b/crates/debugger_tools/src/dap_log.rs index 3b52134401..532107f633 100644 --- a/crates/debugger_tools/src/dap_log.rs +++ b/crates/debugger_tools/src/dap_log.rs @@ -434,9 +434,14 @@ impl LogStore { fn clean_sessions(&mut self, cx: &mut Context) { self.projects.values_mut().for_each(|project| { - project - .debug_sessions - .retain(|_, session| !session.is_terminated); + let mut allowed_terminated_sessions = 10u32; + project.debug_sessions.retain(|_, session| { + if !session.is_terminated { + return true; + } + allowed_terminated_sessions = allowed_terminated_sessions.saturating_sub(1); + allowed_terminated_sessions > 0 + }); }); cx.notify(); diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 58001ce11d..91e49059e9 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -900,7 +900,7 @@ impl RunningState { let config_is_valid = request_type.is_ok(); - + let mut extra_config = Value::Null; let build_output = if let Some(build) = build { let (task_template, locator_name) = match build { BuildTaskDefinition::Template { @@ -930,6 +930,7 @@ impl RunningState { }; let locator_name = if let Some(locator_name) = locator_name { + extra_config = config.clone(); debug_assert!(!config_is_valid); Some(locator_name) } else if !config_is_valid { @@ -945,6 +946,7 @@ impl RunningState { }); if let Ok(t) = task { t.await.and_then(|scenario| { + extra_config = scenario.config; match scenario.build { Some(BuildTaskDefinition::Template { locator_name, .. @@ -1008,13 +1010,13 @@ impl RunningState { if !exit_status.success() { anyhow::bail!("Build failed"); } - Some((task.resolved.clone(), locator_name)) + Some((task.resolved.clone(), locator_name, extra_config)) } else { None }; if config_is_valid { - } else if let Some((task, locator_name)) = build_output { + } else if let Some((task, locator_name, extra_config)) = build_output { let locator_name = locator_name.with_context(|| { format!("Could not find a valid locator for a build task and configure is invalid with error: {}", request_type.err() @@ -1039,6 +1041,8 @@ impl RunningState { .with_context(|| anyhow!("{}: is not a valid adapter name", &adapter))?.config_from_zed_format(zed_config) .await?; config = scenario.config; + util::merge_non_null_json_value_into(extra_config, &mut config); + Self::substitute_variables_in_config(&mut config, &task_context); } else { let Err(e) = request_type else { diff --git a/crates/project/src/debugger/locators/cargo.rs b/crates/project/src/debugger/locators/cargo.rs index 4cf4233ca3..caa563eb6b 100644 --- a/crates/project/src/debugger/locators/cargo.rs +++ b/crates/project/src/debugger/locators/cargo.rs @@ -2,7 +2,7 @@ use anyhow::{Context as _, Result}; use async_trait::async_trait; use dap::{DapLocator, DebugRequest, adapters::DebugAdapterName}; use gpui::SharedString; -use serde_json::Value; +use serde_json::{Value, json}; use smol::{ io::AsyncReadExt, process::{Command, Stdio}, @@ -76,6 +76,13 @@ impl DapLocator for CargoLocator { _ => {} } + let config = if adapter.as_ref() == "CodeLLDB" { + json!({ + "sourceLanguages": ["rust"] + }) + } else { + Value::Null + }; Some(DebugScenario { adapter: adapter.0.clone(), label: resolved_label.to_string().into(), @@ -83,7 +90,7 @@ impl DapLocator for CargoLocator { task_template, locator_name: Some(self.name()), }), - config: serde_json::Value::Null, + config, tcp_connection: None, }) } diff --git a/crates/project/src/debugger/session.rs b/crates/project/src/debugger/session.rs index 255a580e36..837bc4b81c 100644 --- a/crates/project/src/debugger/session.rs +++ b/crates/project/src/debugger/session.rs @@ -1479,6 +1479,28 @@ impl Session { } Events::Capabilities(event) => { self.capabilities = self.capabilities.merge(event.capabilities); + + // The adapter might've enabled new exception breakpoints (or disabled existing ones). + let recent_filters = self + .capabilities + .exception_breakpoint_filters + .iter() + .flatten() + .map(|filter| (filter.filter.clone(), filter.clone())) + .collect::>(); + for filter in recent_filters.values() { + let default = filter.default.unwrap_or_default(); + self.exception_breakpoints + .entry(filter.filter.clone()) + .or_insert_with(|| (filter.clone(), default)); + } + self.exception_breakpoints + .retain(|k, _| recent_filters.contains_key(k)); + if self.is_started() { + self.send_exception_breakpoints(cx); + } + + // Remove the ones that no longer exist. cx.notify(); } Events::Memory(_) => {} diff --git a/docs/src/debugger.md b/docs/src/debugger.md index 37930ac560..47d9ffaa04 100644 --- a/docs/src/debugger.md +++ b/docs/src/debugger.md @@ -214,6 +214,8 @@ requirements.txt #### Rust/C++/C +> For CodeLLDB, you might want to set `sourceLanguages` in your launch configuration based on the source code language. + ##### Using pre-built binary ```json @@ -222,7 +224,7 @@ requirements.txt "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 + "adapter": "CodeLLDB" // GDB is available on non-ARM Macs as well as Linux } ] ``` @@ -239,7 +241,23 @@ requirements.txt }, "program": "$ZED_WORKTREE_ROOT/target/debug/binary", "request": "launch", - "adapter": "CodeLLDB" // GDB is available on non arm macs as well as linux + "adapter": "CodeLLDB" // GDB is available on non-ARM Macs as well as Linux + } +] +``` + +##### Automatically locate a debug target based on build command + +```json +[ + { + "label": "Build & Debug native binary", + "adapter": "CodeLLDB" // GDB is available on non-ARM Macs as well as Linux + // Zed can infer the path to a debuggee based on the build command + "build": { + "command": "cargo", + "args": ["build"] + }, } ] ```