From 9ed94b63963d9bf1c5a2178899962ec3c38cac91 Mon Sep 17 00:00:00 2001 From: Ryan Hawkins Date: Sat, 28 Jun 2025 23:04:29 -0600 Subject: [PATCH 1/4] Add debug locator support Makes it easy to run and debug tests from the sidebar or the new attach modal. --- extension.toml | 1 + languages/ruby/config.toml | 2 +- src/ruby.rs | 62 +++++++++++++++++++++++++++++++++++--- 3 files changed, 60 insertions(+), 5 deletions(-) diff --git a/extension.toml b/extension.toml index 3291148..d6e1cab 100644 --- a/extension.toml +++ b/extension.toml @@ -74,3 +74,4 @@ command = "gem" args = ["update", "--norc", "*"] [debug_adapters.rdbg] +[debug_locators.ruby] diff --git a/languages/ruby/config.toml b/languages/ruby/config.toml index 26692a8..49bbab8 100644 --- a/languages/ruby/config.toml +++ b/languages/ruby/config.toml @@ -61,7 +61,7 @@ collapsed_placeholder = "# ..." tab_size = 2 scope_opt_in_language_servers = ["tailwindcss-language-server"] word_characters = ["?", "!"] -debuggers = ["RDBG"] +debuggers = ["rdbg"] [overrides.string] completion_query_characters = ["-", "."] diff --git a/src/ruby.rs b/src/ruby.rs index 536455c..fae28c2 100644 --- a/src/ruby.rs +++ b/src/ruby.rs @@ -38,6 +38,10 @@ struct RubyDebugConfig { cwd: Option, } +const COMMON_RUBY_COMMANDS: [&str; 7] = [ + "bundle", "rake", "rspec", "minitest", "test", "ruby", "rails", +]; + impl zed::Extension for RubyExtension { fn new() -> Self { Self::default() @@ -172,6 +176,11 @@ impl zed::Extension for RubyExtension { .or_insert_with(|| worktree.root_path().into()); } + if let Some(configuration) = configuration.as_object_mut() { + configuration + .entry("cwd") + .or_insert_with(|| worktree.root_path().into()); + } let ruby_config: RubyDebugConfig = serde_json::from_value(configuration.clone()) .map_err(|e| format!("`config` is not a valid rdbg config: {e}"))?; let mut arguments = vec![]; @@ -222,16 +231,14 @@ impl zed::Extension for RubyExtension { } } - if !arguments.contains(&"--command".to_string()) { - // Ensure that all arguments are passed after a "--", as required by rdbg. - arguments.push("--".into()); - } arguments.extend(ruby_config.args); if use_bundler { arguments.splice(0..0, vec!["exec".to_string(), "rdbg".to_string()]); } + println!("Configuration: {configuration:?}"); + Ok(DebugAdapterBinary { command: Some(rdbg_path.to_string()), arguments, @@ -315,6 +322,53 @@ impl zed::Extension for RubyExtension { } } } + + fn dap_locator_create_scenario( + &mut self, + _locator_name: String, + build_task: zed_extension_api::TaskTemplate, + resolved_label: String, + debug_adapter_name: String, + ) -> Option { + if debug_adapter_name != "rdbg" + || !COMMON_RUBY_COMMANDS + .iter() + // Oftentimes, Ruby projects will have a `bin` directory with an + // executable, hence the contains check. + .any(|cmd| build_task.command.contains(cmd)) + { + return None; + } + + let config = RubyDebugConfig { + script_or_command: None, + script: None, + command: Some(build_task.command), + args: build_task.args, + env: HashMap::from_iter(build_task.env), + cwd: build_task.cwd, + }; + + let config = match serde_json::to_value(config) { + Ok(mut value) => { + if let Some(obj) = value.as_object_mut() { + obj.entry("request").or_insert("launch".into()); + value.to_string() + } else { + return None; + } + } + Err(_) => return None, + }; + + Some(DebugScenario { + adapter: debug_adapter_name, + label: resolved_label, + config, + tcp_connection: None, + build: None, + }) + } } zed_extension_api::register_extension!(RubyExtension); From ff70e493e5af764c40a614b89f31af78b97c0bb0 Mon Sep 17 00:00:00 2001 From: Ryan Hawkins Date: Sun, 29 Jun 2025 09:53:37 -0600 Subject: [PATCH 2/4] Refine locator conditions --- src/ruby.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ruby.rs b/src/ruby.rs index fae28c2..0f90fd9 100644 --- a/src/ruby.rs +++ b/src/ruby.rs @@ -176,11 +176,6 @@ impl zed::Extension for RubyExtension { .or_insert_with(|| worktree.root_path().into()); } - if let Some(configuration) = configuration.as_object_mut() { - configuration - .entry("cwd") - .or_insert_with(|| worktree.root_path().into()); - } let ruby_config: RubyDebugConfig = serde_json::from_value(configuration.clone()) .map_err(|e| format!("`config` is not a valid rdbg config: {e}"))?; let mut arguments = vec![]; @@ -231,14 +226,18 @@ impl zed::Extension for RubyExtension { } } + if let Some(configuration) = configuration.as_object_mut() { + configuration + .entry("cwd") + .or_insert_with(|| worktree.root_path().into()); + } + arguments.extend(ruby_config.args); if use_bundler { arguments.splice(0..0, vec!["exec".to_string(), "rdbg".to_string()]); } - println!("Configuration: {configuration:?}"); - Ok(DebugAdapterBinary { command: Some(rdbg_path.to_string()), arguments, @@ -325,12 +324,13 @@ impl zed::Extension for RubyExtension { fn dap_locator_create_scenario( &mut self, - _locator_name: String, + locator_name: String, build_task: zed_extension_api::TaskTemplate, resolved_label: String, debug_adapter_name: String, ) -> Option { if debug_adapter_name != "rdbg" + || locator_name != "ruby" || !COMMON_RUBY_COMMANDS .iter() // Oftentimes, Ruby projects will have a `bin` directory with an From 33b4a46585eb50d6bc57150943dc0c16a3150761 Mon Sep 17 00:00:00 2001 From: Ryan Hawkins Date: Sun, 29 Jun 2025 23:00:45 -0600 Subject: [PATCH 3/4] Remove command check Users might have some custom script that they want to run that lives in their `bin` directory or on their path. Rather than limit them, trust that they know what they want. --- src/ruby.rs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/ruby.rs b/src/ruby.rs index 0f90fd9..238e2a4 100644 --- a/src/ruby.rs +++ b/src/ruby.rs @@ -38,10 +38,6 @@ struct RubyDebugConfig { cwd: Option, } -const COMMON_RUBY_COMMANDS: [&str; 7] = [ - "bundle", "rake", "rspec", "minitest", "test", "ruby", "rails", -]; - impl zed::Extension for RubyExtension { fn new() -> Self { Self::default() @@ -329,14 +325,7 @@ impl zed::Extension for RubyExtension { resolved_label: String, debug_adapter_name: String, ) -> Option { - if debug_adapter_name != "rdbg" - || locator_name != "ruby" - || !COMMON_RUBY_COMMANDS - .iter() - // Oftentimes, Ruby projects will have a `bin` directory with an - // executable, hence the contains check. - .any(|cmd| build_task.command.contains(cmd)) - { + if debug_adapter_name != "rdbg" || locator_name != "ruby" { return None; } From 0affacd9e7757138a0fdf9021955cdf1dccc6c28 Mon Sep 17 00:00:00 2001 From: Ryan Hawkins Date: Tue, 1 Jul 2025 11:19:23 -0600 Subject: [PATCH 4/4] Fix rebase issues During rebase I accidentally deleted code from a previous PR. Whoops. --- src/ruby.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/ruby.rs b/src/ruby.rs index 238e2a4..987655e 100644 --- a/src/ruby.rs +++ b/src/ruby.rs @@ -222,12 +222,10 @@ impl zed::Extension for RubyExtension { } } - if let Some(configuration) = configuration.as_object_mut() { - configuration - .entry("cwd") - .or_insert_with(|| worktree.root_path().into()); + if !arguments.contains(&"--command".to_string()) { + // Ensure that all arguments are passed after a "--", as required by rdbg. + arguments.push("--".into()); } - arguments.extend(ruby_config.args); if use_bundler {