use std::path::Path; use anyhow::{Result, bail}; use async_trait::async_trait; use dap::{DapLocator, DebugRequest, adapters::DebugAdapterName}; use gpui::SharedString; use task::{DebugScenario, SpawnInTerminal, TaskTemplate, VariableName}; pub(crate) struct PythonLocator; #[async_trait] impl DapLocator for PythonLocator { fn name(&self) -> SharedString { SharedString::new_static("Python") } /// Determines whether this locator can generate debug target for given task. async fn create_scenario( &self, build_config: &TaskTemplate, resolved_label: &str, adapter: &DebugAdapterName, ) -> Option { if adapter.0.as_ref() != "Debugpy" { return None; } let valid_program = build_config.command.starts_with("$ZED_") || Path::new(&build_config.command) .file_name() .map_or(false, |name| { name.to_str().is_some_and(|path| path.starts_with("python")) }); if !valid_program || build_config.args.iter().any(|arg| arg == "-c") { // We cannot debug selections. return None; } let command = if build_config.command == VariableName::Custom("PYTHON_ACTIVE_ZED_TOOLCHAIN".into()).template_value() { VariableName::Custom("PYTHON_ACTIVE_ZED_TOOLCHAIN_RAW".into()).template_value() } else { build_config.command.clone() }; let module_specifier_position = build_config .args .iter() .position(|arg| arg == "-m") .map(|position| position + 1); // Skip the -m and module name, get all that's after. let mut rest_of_the_args = module_specifier_position .and_then(|position| build_config.args.get(position..)) .into_iter() .flatten() .fuse(); let mod_name = rest_of_the_args.next(); let args = rest_of_the_args.collect::>(); let program_position = mod_name .is_none() .then(|| { build_config .args .iter() .position(|arg| *arg == "\"$ZED_FILE\"") }) .flatten(); let args = if let Some(position) = program_position { args.into_iter().skip(position).collect::>() } else { args }; if program_position.is_none() && mod_name.is_none() { return None; } let mut config = serde_json::json!({ "request": "launch", "python": command, "args": args, "cwd": build_config.cwd.clone() }); if let Some(config_obj) = config.as_object_mut() { if let Some(module) = mod_name { config_obj.insert("module".to_string(), module.clone().into()); } if let Some(program) = program_position { config_obj.insert( "program".to_string(), build_config.args[program].clone().into(), ); } } Some(DebugScenario { adapter: adapter.0.clone(), label: resolved_label.to_string().into(), build: None, config, tcp_connection: None, }) } async fn run(&self, _: SpawnInTerminal) -> Result { bail!("Python locator should not require DapLocator::run to be ran"); } }