diff --git a/Cargo.lock b/Cargo.lock index 6042566..edb1c11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,6 +32,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "cfg-if" version = "1.0.3" @@ -178,6 +184,18 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -323,12 +341,28 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "leb128fmt" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + [[package]] name = "litemap" version = "0.8.0" @@ -417,6 +451,18 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.20" @@ -559,6 +605,94 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "getrandom", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen 0.46.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + [[package]] name = "wasm-encoder" version = "0.227.1" @@ -610,6 +744,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "wit-bindgen-core" version = "0.41.0" @@ -732,19 +872,21 @@ dependencies = [ [[package]] name = "zed_extension_api" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ee021e3a4c69d6ae90137fcf7537d1a8a5032dc9bf180c8fa6dd1a2f7c56d7" +checksum = "0729d50b4ca0a7e28e590bbe32e3ca0194d97ef654961451a424c661a366fca0" dependencies = [ "serde", "serde_json", - "wit-bindgen", + "wit-bindgen 0.41.0", ] [[package]] name = "zed_zig" version = "0.3.5" dependencies = [ + "serde_json", + "uuid", "zed_extension_api", ] diff --git a/Cargo.toml b/Cargo.toml index 9990282..23c5724 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,6 @@ path = "src/zig.rs" crate-type = ["cdylib"] [dependencies] -zed_extension_api = "0.6.0" +serde_json = "1.0" +uuid = { version = "1.1.2", features = ["v4"] } +zed_extension_api = "0.7.0" diff --git a/extension.toml b/extension.toml index 7d97ee4..40325a0 100644 --- a/extension.toml +++ b/extension.toml @@ -13,3 +13,5 @@ language = "Zig" [grammars.zig] repository = "https://github.com/tree-sitter-grammars/tree-sitter-zig" commit = "6479aa13f32f701c383083d8b28360ebd682fb7d" + +[debug_locators.zig-locator] diff --git a/languages/zig/runnables.scm b/languages/zig/runnables.scm index 0d66620..94244fd 100644 --- a/languages/zig/runnables.scm +++ b/languages/zig/runnables.scm @@ -1,7 +1,7 @@ ; Tag unit tests ( (test_declaration - (string) @name @ZIG_TEST_NAME + (string (string_content) @name @ZIG_TEST_NAME) ) @run (#set! tag zig-test) ) diff --git a/languages/zig/tasks.json b/languages/zig/tasks.json index 225c4ec..c142bed 100644 --- a/languages/zig/tasks.json +++ b/languages/zig/tasks.json @@ -13,7 +13,12 @@ { "label": "zig test", "command": "zig", - "args": ["test", "$ZED_FILE", "--test-filter", "$ZED_CUSTOM_ZIG_TEST_NAME"], + "args": [ + "test", + "$ZED_FILE", + "--test-filter", + "\"$ZED_CUSTOM_ZIG_TEST_NAME\"" + ], "tags": ["zig-test"] } ] diff --git a/src/zig.rs b/src/zig.rs index 43aab22..ec19b42 100644 --- a/src/zig.rs +++ b/src/zig.rs @@ -1,6 +1,8 @@ -use std::fs; +use std::{fs, path::Path}; use zed_extension_api::{self as zed, serde_json, settings::LspSettings, LanguageServerId, Result}; +const ZIG_TEST_EXE_BASENAME: &str = "zig_test"; + struct ZigExtension { cached_binary_path: Option, } @@ -166,6 +168,141 @@ impl zed::Extension for ZigExtension { .unwrap_or_default(); Ok(Some(settings)) } + + fn dap_locator_create_scenario( + &mut self, + locator_name: String, + build_task: zed::TaskTemplate, + resolved_label: String, + debug_adapter_name: String, + ) -> Option { + if build_task.command != "zig" { + return None; + } + + let cwd = build_task.cwd.clone(); + let env = build_task.env.clone().into_iter().collect(); + + let mut args_it = build_task.args.iter(); + let template = match args_it.next() { + Some(arg) if arg == "build" => match args_it.next() { + Some(arg) if arg == "run" => zed::BuildTaskTemplate { + label: "zig build".into(), + command: "zig".into(), + args: vec!["build".into()], + env, + cwd, + }, + _ => return None, + }, + Some(arg) if arg == "test" => { + let test_exe_path = get_test_exe_path().unwrap(); + let mut args: Vec = build_task + .args + .into_iter() + // TODO verify if this is required on non-Windows platforms + .map(|s| s.replace("\"", "'")) + .collect(); + args.push("--test-no-exec".into()); + args.push(format!("-femit-bin={test_exe_path}")); + + zed::BuildTaskTemplate { + label: "zig test --test-no-exec".into(), + command: "zig".into(), + args, + env, + cwd, + } + } + _ => return None, + }; + + let config = serde_json::Value::Null; + let Ok(config) = serde_json::to_string(&config) else { + return None; + }; + + Some(zed::DebugScenario { + adapter: debug_adapter_name, + label: resolved_label.clone(), + config, + tcp_connection: None, + build: Some(zed::BuildTaskDefinition::Template( + zed::BuildTaskDefinitionTemplatePayload { + template, + locator_name: Some(locator_name.into()), + }, + )), + }) + } + + fn run_dap_locator( + &mut self, + _locator_name: String, + build_task: zed::TaskTemplate, + ) -> Result { + let mut args_it = build_task.args.iter(); + match args_it.next() { + Some(arg) if arg == "build" => { + // We only handle the default case where the binary name matches the project name. + // This is valid for projects created with `zig init`. + // In other cases, the user should provide a custom debug configuration. + let exec = get_project_name(&build_task).ok_or("Failed to get project name")?; + + let request = zed::LaunchRequest { + program: format!("zig-out/bin/{exec}"), + cwd: build_task.cwd, + args: vec![], + envs: build_task.env.into_iter().collect(), + }; + + Ok(zed::DebugRequest::Launch(request)) + } + Some(arg) if arg == "test" => { + let program = build_task + .args + .iter() + .find_map(|arg| { + arg.strip_prefix("-femit-bin=").map(|arg| { + arg.split("=") + .nth(1) + .ok_or("Expected binary path in -femit-bin=") + .map(|path| path.trim_end_matches(".exe")) + }) + }) + .ok_or("Failed to extract binary path from command args") + .flatten()? + .to_string(); + let request = zed::LaunchRequest { + program, + cwd: build_task.cwd, + args: vec![], + envs: build_task.env.into_iter().collect(), + }; + Ok(zed::DebugRequest::Launch(request)) + } + _ => Err("Unsupported build task".into()), + } + } +} + +fn get_project_name(task: &zed::TaskTemplate) -> Option { + task.cwd + .as_ref() + .and_then(|cwd| Some(Path::new(&cwd).file_name()?.to_string_lossy().into_owned())) +} + +fn get_test_exe_path() -> Option { + let test_exe_dir = std::env::current_dir().ok()?; + let mut name = format!( + "{}_{}", + ZIG_TEST_EXE_BASENAME, + uuid::Uuid::new_v4().to_string() + ); + if zed::current_platform().0 == zed::Os::Windows { + name.push_str(".exe"); + } + Some(test_exe_dir.join(name).to_string_lossy().into_owned()) } zed::register_extension!(ZigExtension);