From bab222c67bbaad23a4dd6d28a8565b83607ca0ac Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Sun, 17 May 2026 23:39:04 -0500 Subject: [PATCH 1/5] fix: Support single-level packages in runnables The tree-sitter query for runnables was only capturing `scoped_identifier` for package names. This caused it to completely miss single-level packages, e.g., `package example;`, which are parsed as a basic `identifier`. This updates the query to match both `identifier` and `scoped_identifier`. --- Cargo.lock | 360 +++++++++++++++++- Cargo.toml | 5 + languages/java/runnables.scm | 30 +- tests/java.rs | 19 + tests/languages/java/example/Main.java | 7 + tests/languages/java/org/example/Main.java | 7 + .../java/snapshots/runnables_multi_level.snap | 57 +++ .../snapshots/runnables_single_level.snap | 57 +++ tests/support/mod.rs | 84 ++++ 9 files changed, 603 insertions(+), 23 deletions(-) create mode 100644 tests/java.rs create mode 100644 tests/languages/java/example/Main.java create mode 100644 tests/languages/java/org/example/Main.java create mode 100644 tests/languages/java/snapshots/runnables_multi_level.snap create mode 100644 tests/languages/java/snapshots/runnables_single_level.snap create mode 100644 tests/support/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 65c8994..368c0e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,12 +50,33 @@ dependencies = [ "generic-array", ] +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "console" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" +dependencies = [ + "encode_unicode", + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -105,12 +126,40 @@ dependencies = [ "syn", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + [[package]] name = "flate2" version = "1.1.2" @@ -235,6 +284,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -380,6 +442,19 @@ dependencies = [ "serde", ] +[[package]] +name = "insta" +version = "1.47.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" +dependencies = [ + "console", + "once_cell", + "serde", + "similar", + "tempfile", +] + [[package]] name = "itoa" version = "1.0.15" @@ -393,7 +468,7 @@ dependencies = [ "libc", "serde", "serde_json", - "windows-sys", + "windows-sys 0.59.0", ] [[package]] @@ -408,6 +483,12 @@ version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" version = "0.8.0" @@ -496,6 +577,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "regex" version = "1.12.2" @@ -525,6 +612,19 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3160422bbd54dd5ecfdca71e5fd59b7b8fe2b1697ab2baf64f6d05dcc66d298" +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "ryu" version = "1.0.20" @@ -576,6 +676,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ + "indexmap", "itoa", "memchr", "ryu", @@ -594,6 +695,18 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "slab" version = "0.4.11" @@ -621,6 +734,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" + [[package]] name = "syn" version = "2.0.106" @@ -643,6 +762,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tempfile" +version = "3.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -659,6 +791,36 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" +[[package]] +name = "tree-sitter" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887bd495d0582c5e3e0d8ece2233666169fa56a9644d172fc22ad179ab2d0538" +dependencies = [ + "cc", + "regex", + "regex-syntax", + "serde_json", + "streaming-iterator", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-java" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-language" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782" + [[package]] name = "typenum" version = "1.19.0" @@ -701,6 +863,24 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + [[package]] name = "wasm-encoder" version = "0.227.1" @@ -708,7 +888,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80bb72f02e7fbf07183443b27b0f3d4144abf8c114189f2e088ed95b696a7822" dependencies = [ "leb128fmt", - "wasmparser", + "wasmparser 0.227.1", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser 0.244.0", ] [[package]] @@ -726,8 +916,20 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.227.1", + "wasmparser 0.227.1", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", ] [[package]] @@ -742,6 +944,24 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + [[package]] name = "windows-sys" version = "0.59.0" @@ -751,6 +971,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -822,9 +1051,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10fb6648689b3929d56bbc7eb1acf70c9a42a29eb5358c67c10f54dbd5d695de" dependencies = [ "wit-bindgen-rt", - "wit-bindgen-rust-macro", + "wit-bindgen-rust-macro 0.41.0", ] +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro 0.51.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.41.0" @@ -833,7 +1077,18 @@ checksum = "92fa781d4f2ff6d3f27f3cc9b74a73327b31ca0dc4a3ef25a0ce2983e0e5af9b" dependencies = [ "anyhow", "heck", - "wit-parser", + "wit-parser 0.227.1", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser 0.244.0", ] [[package]] @@ -858,9 +1113,25 @@ dependencies = [ "indexmap", "prettyplease", "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", + "wasm-metadata 0.227.1", + "wit-bindgen-core 0.41.0", + "wit-component 0.227.1", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata 0.244.0", + "wit-bindgen-core 0.51.0", + "wit-component 0.244.0", ] [[package]] @@ -874,8 +1145,23 @@ dependencies = [ "proc-macro2", "quote", "syn", - "wit-bindgen-core", - "wit-bindgen-rust", + "wit-bindgen-core 0.41.0", + "wit-bindgen-rust 0.41.0", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core 0.51.0", + "wit-bindgen-rust 0.51.0", ] [[package]] @@ -891,10 +1177,29 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", + "wasm-encoder 0.227.1", + "wasm-metadata 0.227.1", + "wasmparser 0.227.1", + "wit-parser 0.227.1", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder 0.244.0", + "wasm-metadata 0.244.0", + "wasmparser 0.244.0", + "wit-parser 0.244.0", ] [[package]] @@ -912,7 +1217,25 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.227.1", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.244.0", ] [[package]] @@ -953,7 +1276,7 @@ checksum = "0729d50b4ca0a7e28e590bbe32e3ca0194d97ef654961451a424c661a366fca0" dependencies = [ "serde", "serde_json", - "wit-bindgen", + "wit-bindgen 0.41.0", ] [[package]] @@ -961,11 +1284,14 @@ name = "zed_java" version = "6.8.18" dependencies = [ "hex", + "insta", "percent-encoding", "regex", "serde", "serde_json", "sha1", + "tree-sitter", + "tree-sitter-java", "zed_extension_api", ] diff --git a/Cargo.toml b/Cargo.toml index 09f6e98..672c397 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,8 @@ serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" sha1 = "0.10.6" zed_extension_api = "0.7.0" + +[dev-dependencies] +tree-sitter = "0.26.8" +tree-sitter-java = "0.23.5" +insta = { version = "1.46", features = ["yaml"] } diff --git a/languages/java/runnables.scm b/languages/java/runnables.scm index e4facbe..b4e60c0 100644 --- a/languages/java/runnables.scm +++ b/languages/java/runnables.scm @@ -1,6 +1,9 @@ ; Run the main function ((package_declaration - (scoped_identifier) @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration (modifiers) @class-modifier (#match? @class-modifier "public") @@ -16,7 +19,10 @@ ; Run the main class ((package_declaration - (scoped_identifier) @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration (modifiers) @class-modifier (#match? @class-modifier "public") @@ -32,7 +38,10 @@ ; Run test function (marker annotation, e.g. @Test) ((package_declaration - (scoped_identifier) @java_package_name) + [ + (identifier) + (scoped_identifier) + ] @java_package_name) (class_declaration name: (identifier) @java_class_name body: (class_body @@ -50,7 +59,10 @@ ; Run nested test function ((package_declaration - (scoped_identifier) @java_package_name) + [ + (identifier) + (scoped_identifier) + ] @java_package_name) (class_declaration name: (identifier) @java_outer_class_name body: (class_body @@ -75,7 +87,10 @@ ; Run test class ((package_declaration - (scoped_identifier) @java_package_name) + [ + (identifier) + (scoped_identifier) + ] @java_package_name) (class_declaration name: (identifier) @java_class_name @run body: (class_body @@ -92,7 +107,10 @@ ; Run nested test class ((package_declaration - (scoped_identifier) @java_package_name) + [ + (identifier) + (scoped_identifier) + ] @java_package_name) (class_declaration name: (identifier) @java_outer_class_name body: (class_body diff --git a/tests/java.rs b/tests/java.rs new file mode 100644 index 0000000..37288a3 --- /dev/null +++ b/tests/java.rs @@ -0,0 +1,19 @@ +mod support; + +#[test] +fn runnables_multi_level_package() { + support::assert_query_snapshot( + "runnables_multi_level", + "tests/languages/java/org/example/Main.java", + "languages/java/runnables.scm", + ); +} + +#[test] +fn runnables_single_level_package() { + support::assert_query_snapshot( + "runnables_single_level", + "tests/languages/java/example/Main.java", + "languages/java/runnables.scm", + ); +} diff --git a/tests/languages/java/example/Main.java b/tests/languages/java/example/Main.java new file mode 100644 index 0000000..382970d --- /dev/null +++ b/tests/languages/java/example/Main.java @@ -0,0 +1,7 @@ +package example; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} diff --git a/tests/languages/java/org/example/Main.java b/tests/languages/java/org/example/Main.java new file mode 100644 index 0000000..9a3edc1 --- /dev/null +++ b/tests/languages/java/org/example/Main.java @@ -0,0 +1,7 @@ +package org.example; + +public class Main { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} diff --git a/tests/languages/java/snapshots/runnables_multi_level.snap b/tests/languages/java/snapshots/runnables_multi_level.snap new file mode 100644 index 0000000..31c955c --- /dev/null +++ b/tests/languages/java/snapshots/runnables_multi_level.snap @@ -0,0 +1,57 @@ +--- +source: tests/support/mod.rs +assertion_line: 126 +expression: captures +--- +- name: java_package_name + line: 1 + column: 9 + text: org.example +- name: _ + line: 3 + column: 1 + text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" +- name: class-modifier + line: 3 + column: 1 + text: public +- name: java_class_name + line: 3 + column: 14 + text: Main +- name: modifier + line: 4 + column: 5 + text: public static +- name: run + line: 4 + column: 24 + text: main +- name: java_package_name + line: 1 + column: 9 + text: org.example +- name: _ + line: 3 + column: 1 + text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" +- name: class-modifier + line: 3 + column: 1 + text: public +- name: java_class_name + line: 3 + column: 14 + text: Main +- name: run + line: 3 + column: 14 + text: Main +- name: modifier + line: 4 + column: 5 + text: public static +- name: method_name + line: 4 + column: 24 + text: main diff --git a/tests/languages/java/snapshots/runnables_single_level.snap b/tests/languages/java/snapshots/runnables_single_level.snap new file mode 100644 index 0000000..26550d8 --- /dev/null +++ b/tests/languages/java/snapshots/runnables_single_level.snap @@ -0,0 +1,57 @@ +--- +source: tests/support/mod.rs +assertion_line: 126 +expression: captures +--- +- name: java_package_name + line: 1 + column: 9 + text: example +- name: _ + line: 3 + column: 1 + text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" +- name: class-modifier + line: 3 + column: 1 + text: public +- name: java_class_name + line: 3 + column: 14 + text: Main +- name: modifier + line: 4 + column: 5 + text: public static +- name: run + line: 4 + column: 24 + text: main +- name: java_package_name + line: 1 + column: 9 + text: example +- name: _ + line: 3 + column: 1 + text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" +- name: class-modifier + line: 3 + column: 1 + text: public +- name: java_class_name + line: 3 + column: 14 + text: Main +- name: run + line: 3 + column: 14 + text: Main +- name: modifier + line: 4 + column: 5 + text: public static +- name: method_name + line: 4 + column: 24 + text: main diff --git a/tests/support/mod.rs b/tests/support/mod.rs new file mode 100644 index 0000000..d5cc056 --- /dev/null +++ b/tests/support/mod.rs @@ -0,0 +1,84 @@ +use std::path::Path; +use tree_sitter::{Parser, Query, QueryCursor, StreamingIterator}; + +/// Represents a single capture from a query match +#[derive(Debug, serde::Serialize)] +pub struct Capture { + pub name: String, + pub line: usize, + pub column: usize, + pub text: String, +} + +fn normalize_line_endings(text: &str) -> String { + text.replace("\r\n", "\n").replace('\r', "\n") +} + +pub fn run_query(source: &str, query_source: &str) -> Vec { + let mut parser = Parser::new(); + let language = tree_sitter_java::LANGUAGE.into(); + parser + .set_language(&language) + .unwrap_or_else(|_| panic!("Error loading Java parser")); + + let tree = parser.parse(source, None).expect("Failed to parse source"); + let query = Query::new(&language, query_source).expect("Failed to create query"); + + let mut cursor = QueryCursor::new(); + let mut captures = Vec::new(); + let source_bytes = source.as_bytes(); + + let mut matches = cursor.matches(&query, tree.root_node(), source_bytes); + while let Some(match_) = matches.next() { + for capture in match_.captures { + let capture_name = &query.capture_names()[capture.index as usize]; + let node = capture.node; + let start = node.start_position(); + let text = node + .utf8_text(source_bytes) + .expect("Failed to extract capture text"); + + captures.push(Capture { + name: capture_name.to_string(), + line: start.row + 1, + column: start.column + 1, + text: text.to_string(), + }); + } + } + + captures +} + +pub fn assert_query_snapshot(snapshot_name: &str, fixture_path: &str, query_path: &str) { + let fixture_abs = Path::new(env!("CARGO_MANIFEST_DIR")).join(fixture_path); + let query_abs = Path::new(env!("CARGO_MANIFEST_DIR")).join(query_path); + let source = std::fs::read_to_string(&fixture_abs).unwrap_or_else(|error| { + panic!( + "Failed to read query fixture file {}: {error}", + fixture_abs.display() + ) + }); + let query_source = std::fs::read_to_string(&query_abs).unwrap_or_else(|error| { + panic!("Failed to read query file {}: {error}", query_abs.display()) + }); + + let captures = run_query( + &normalize_line_endings(&source), + &normalize_line_endings(&query_source), + ); + + let snapshot_dir = Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("languages") + .join("java") + .join("snapshots"); + std::fs::create_dir_all(&snapshot_dir).expect("Failed to create snapshot directory"); + + let mut settings = insta::Settings::clone_current(); + settings.set_snapshot_path(snapshot_dir); + settings.set_prepend_module_to_snapshot(false); + settings.bind(|| { + insta::assert_yaml_snapshot!(snapshot_name, captures); + }); +} From 5d8b699e61bbbb121677d329b3a63438563c5c84 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Mon, 18 May 2026 04:56:32 -0500 Subject: [PATCH 2/5] fix: support single-level and default packages for all runnables --- languages/java/runnables.scm | 48 +++---- languages/java/tasks.json | 6 +- tests/java.rs | 48 ++++++- tests/languages/java/DefaultPackage.java | 6 + tests/languages/java/DefaultPackageTest.java | 6 + .../{Main.java => SingleLevelPackage.java} | 2 +- .../java/example/SingleLevelTest.java | 8 ++ .../{Main.java => MultiLevelPackage.java} | 2 +- .../java/org/example/MultiLevelTest.java | 8 ++ .../snapshots/runnables_default_package.snap | 49 +++++++ .../runnables_default_package_test.snap | 41 ++++++ .../java/snapshots/runnables_multi_level.snap | 12 +- .../snapshots/runnables_multi_level_test.snap | 49 +++++++ .../snapshots/runnables_single_level.snap | 12 +- .../runnables_single_level_test.snap | 49 +++++++ tests/task_verification_test.rs | 131 ++++++++++++++++++ 16 files changed, 428 insertions(+), 49 deletions(-) create mode 100644 tests/languages/java/DefaultPackage.java create mode 100644 tests/languages/java/DefaultPackageTest.java rename tests/languages/java/example/{Main.java => SingleLevelPackage.java} (77%) create mode 100644 tests/languages/java/example/SingleLevelTest.java rename tests/languages/java/org/example/{Main.java => MultiLevelPackage.java} (78%) create mode 100644 tests/languages/java/org/example/MultiLevelTest.java create mode 100644 tests/languages/java/snapshots/runnables_default_package.snap create mode 100644 tests/languages/java/snapshots/runnables_default_package_test.snap create mode 100644 tests/languages/java/snapshots/runnables_multi_level_test.snap create mode 100644 tests/languages/java/snapshots/runnables_single_level_test.snap diff --git a/languages/java/runnables.scm b/languages/java/runnables.scm index b4e60c0..d3f9f07 100644 --- a/languages/java/runnables.scm +++ b/languages/java/runnables.scm @@ -1,9 +1,7 @@ ; Run the main function -((package_declaration - [ - (identifier) - (scoped_identifier) - ] @java_package_name)? +(program + (package_declaration + [(identifier) (scoped_identifier)] @java_package_name)? (class_declaration (modifiers) @class-modifier (#match? @class-modifier "public") @@ -18,11 +16,9 @@ (#set! tag java-main)) ; Run the main class -((package_declaration - [ - (identifier) - (scoped_identifier) - ] @java_package_name)? +(program + (package_declaration + [(identifier) (scoped_identifier)] @java_package_name)? (class_declaration (modifiers) @class-modifier (#match? @class-modifier "public") @@ -37,11 +33,9 @@ (#set! tag java-main)) ; Run test function (marker annotation, e.g. @Test) -((package_declaration - [ - (identifier) - (scoped_identifier) - ] @java_package_name) +(program + (package_declaration + [(identifier) (scoped_identifier)] @java_package_name)? (class_declaration name: (identifier) @java_class_name body: (class_body @@ -58,11 +52,9 @@ (#set! tag java-test-method)) ; Run nested test function -((package_declaration - [ - (identifier) - (scoped_identifier) - ] @java_package_name) +(program + (package_declaration + [(identifier) (scoped_identifier)] @java_package_name)? (class_declaration name: (identifier) @java_outer_class_name body: (class_body @@ -86,11 +78,9 @@ (#set! tag java-test-method-nested)) ; Run test class -((package_declaration - [ - (identifier) - (scoped_identifier) - ] @java_package_name) +(program + (package_declaration + [(identifier) (scoped_identifier)] @java_package_name)? (class_declaration name: (identifier) @java_class_name @run body: (class_body @@ -106,11 +96,9 @@ (#set! tag java-test-class)) ; Run nested test class -((package_declaration - [ - (identifier) - (scoped_identifier) - ] @java_package_name) +(program + (package_declaration + [(identifier) (scoped_identifier)] @java_package_name)? (class_declaration name: (identifier) @java_outer_class_name body: (class_body diff --git a/languages/java/tasks.json b/languages/java/tasks.json index bf98f71..41c8cae 100644 --- a/languages/java/tasks.json +++ b/languages/java/tasks.json @@ -1,7 +1,7 @@ [ { "label": "Run $ZED_CUSTOM_java_class_name", - "command": "pkg=\"$ZED_CUSTOM_java_package_name\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; case \"$f\" in *\"/src/test/\"*) COMPILE_GOAL=\"test-compile\"; CLASSPATH_SCOPE=\"test\";; *) COMPILE_GOAL=\"compile\"; CLASSPATH_SCOPE=\"runtime\";; esac; if [ \"$m\" = \".\" ]; then $CMD clean $COMPILE_GOAL exec:java -Dexec.mainClass=\"$c\" -Dexec.classpathScope=$CLASSPATH_SCOPE; else $CMD clean $COMPILE_GOAL -pl \"$m\" -am && $CMD exec:java -pl \"$m\" -Dexec.mainClass=\"$c\" -Dexec.classpathScope=$CLASSPATH_SCOPE; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;", + "command": "pkg=\"${ZED_CUSTOM_java_package_name:-}\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; case \"$f\" in *\"/src/test/\"*) COMPILE_GOAL=\"test-compile\"; CLASSPATH_SCOPE=\"test\";; *) COMPILE_GOAL=\"compile\"; CLASSPATH_SCOPE=\"runtime\";; esac; if [ \"$m\" = \".\" ]; then $CMD clean $COMPILE_GOAL exec:java -Dexec.mainClass=\"$c\" -Dexec.classpathScope=$CLASSPATH_SCOPE; else $CMD clean $COMPILE_GOAL -pl \"$m\" -am && $CMD exec:java -pl \"$m\" -Dexec.mainClass=\"$c\" -Dexec.classpathScope=$CLASSPATH_SCOPE; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;", "use_new_terminal": false, "reveal": "always", "tags": ["java-main"], @@ -14,7 +14,7 @@ }, { "label": "$ZED_CUSTOM_java_class_name.${ZED_CUSTOM_java_outer_class_name:}.$ZED_CUSTOM_java_method_name", - "command": "package=\"$ZED_CUSTOM_java_package_name\"; outer=\"${ZED_CUSTOM_java_outer_class_name:}\"; inner=\"$ZED_CUSTOM_java_class_name\"; method=\"$ZED_CUSTOM_java_method_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test -Dtest=\"$package.$c#$method\"; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\" -Dtest=\"$package.$c#$method\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test --tests \"$package.$c.$method\"; else >&2 echo 'No build tool found'; exit 1; fi;", + "command": "package=\"${ZED_CUSTOM_java_package_name:-}\"; outer=\"${ZED_CUSTOM_java_outer_class_name:-}\"; inner=\"$ZED_CUSTOM_java_class_name\"; method=\"$ZED_CUSTOM_java_method_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; if [ -n \"$package\" ]; then fqc=\"$package.$c\"; else fqc=\"$c\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test -Dtest=\"$fqc#$method\"; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\" -Dtest=\"$fqc#$method\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test --tests \"$fqc.$method\"; else >&2 echo 'No build tool found'; exit 1; fi;", "use_new_terminal": false, "reveal": "always", "tags": ["java-test-method", "java-test-method-nested"], @@ -27,7 +27,7 @@ }, { "label": "Test class $ZED_CUSTOM_java_class_name", - "command": "package=\"$ZED_CUSTOM_java_package_name\"; outer=\"${ZED_CUSTOM_java_outer_class_name:}\"; inner=\"$ZED_CUSTOM_java_class_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test -Dtest=\"$package.$c\"; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\" -Dtest=\"$package.$c\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test --tests \"$package.$c\"; else >&2 echo 'No build tool found'; exit 1; fi;", + "command": "package=\"${ZED_CUSTOM_java_package_name:-}\"; outer=\"${ZED_CUSTOM_java_outer_class_name:-}\"; inner=\"$ZED_CUSTOM_java_class_name\"; sep=\"$\"; if [ -n \"$outer\" ]; then c=\"$outer$sep$inner\"; else c=\"$inner\"; fi; if [ -n \"$package\" ]; then fqc=\"$package.$c\"; else fqc=\"$c\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; if [ \"$m\" = \".\" ]; then $CMD clean test -Dtest=\"$fqc\"; else $CMD clean test-compile -pl \"$m\" -am && $CMD test -pl \"$m\" -Dtest=\"$fqc\"; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:test --tests \"$fqc\"; else >&2 echo 'No build tool found'; exit 1; fi;", "use_new_terminal": false, "reveal": "always", "tags": ["java-test-class", "java-test-class-nested"], diff --git a/tests/java.rs b/tests/java.rs index 37288a3..c01bd51 100644 --- a/tests/java.rs +++ b/tests/java.rs @@ -1,10 +1,14 @@ mod support; +// ============================================================================ +// Main Method Tests +// ============================================================================ + #[test] fn runnables_multi_level_package() { support::assert_query_snapshot( "runnables_multi_level", - "tests/languages/java/org/example/Main.java", + "tests/languages/java/org/example/MultiLevelPackage.java", "languages/java/runnables.scm", ); } @@ -13,7 +17,47 @@ fn runnables_multi_level_package() { fn runnables_single_level_package() { support::assert_query_snapshot( "runnables_single_level", - "tests/languages/java/example/Main.java", + "tests/languages/java/example/SingleLevelPackage.java", + "languages/java/runnables.scm", + ); +} + +#[test] +fn runnables_default_package() { + support::assert_query_snapshot( + "runnables_default_package", + "tests/languages/java/DefaultPackage.java", + "languages/java/runnables.scm", + ); +} + +// ============================================================================ +// JUnit Test Tests +// ============================================================================ + +#[test] +fn runnables_multi_level_test() { + support::assert_query_snapshot( + "runnables_multi_level_test", + "tests/languages/java/org/example/MultiLevelTest.java", + "languages/java/runnables.scm", + ); +} + +#[test] +fn runnables_single_level_test() { + support::assert_query_snapshot( + "runnables_single_level_test", + "tests/languages/java/example/SingleLevelTest.java", + "languages/java/runnables.scm", + ); +} + +#[test] +fn runnables_default_package_test() { + support::assert_query_snapshot( + "runnables_default_package_test", + "tests/languages/java/DefaultPackageTest.java", "languages/java/runnables.scm", ); } diff --git a/tests/languages/java/DefaultPackage.java b/tests/languages/java/DefaultPackage.java new file mode 100644 index 0000000..695ea92 --- /dev/null +++ b/tests/languages/java/DefaultPackage.java @@ -0,0 +1,6 @@ +public class DefaultPackage { + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} + diff --git a/tests/languages/java/DefaultPackageTest.java b/tests/languages/java/DefaultPackageTest.java new file mode 100644 index 0000000..116a6c4 --- /dev/null +++ b/tests/languages/java/DefaultPackageTest.java @@ -0,0 +1,6 @@ +import org.junit.jupiter.api.Test; + +public class DefaultPackageTest { + @Test + void testMethod() {} +} diff --git a/tests/languages/java/example/Main.java b/tests/languages/java/example/SingleLevelPackage.java similarity index 77% rename from tests/languages/java/example/Main.java rename to tests/languages/java/example/SingleLevelPackage.java index 382970d..3c57ca3 100644 --- a/tests/languages/java/example/Main.java +++ b/tests/languages/java/example/SingleLevelPackage.java @@ -1,6 +1,6 @@ package example; -public class Main { +public class SingleLevelPackage { public static void main(String[] args) { System.out.println("Hello world!"); } diff --git a/tests/languages/java/example/SingleLevelTest.java b/tests/languages/java/example/SingleLevelTest.java new file mode 100644 index 0000000..7db07d6 --- /dev/null +++ b/tests/languages/java/example/SingleLevelTest.java @@ -0,0 +1,8 @@ +package example; + +import org.junit.jupiter.api.Test; + +public class SingleLevelTest { + @Test + void testMethod() {} +} diff --git a/tests/languages/java/org/example/Main.java b/tests/languages/java/org/example/MultiLevelPackage.java similarity index 78% rename from tests/languages/java/org/example/Main.java rename to tests/languages/java/org/example/MultiLevelPackage.java index 9a3edc1..0e97185 100644 --- a/tests/languages/java/org/example/Main.java +++ b/tests/languages/java/org/example/MultiLevelPackage.java @@ -1,6 +1,6 @@ package org.example; -public class Main { +public class MultiLevelPackage { public static void main(String[] args) { System.out.println("Hello world!"); } diff --git a/tests/languages/java/org/example/MultiLevelTest.java b/tests/languages/java/org/example/MultiLevelTest.java new file mode 100644 index 0000000..7da3fef --- /dev/null +++ b/tests/languages/java/org/example/MultiLevelTest.java @@ -0,0 +1,8 @@ +package org.example; + +import org.junit.jupiter.api.Test; + +public class MultiLevelTest { + @Test + void testMethod() {} +} diff --git a/tests/languages/java/snapshots/runnables_default_package.snap b/tests/languages/java/snapshots/runnables_default_package.snap new file mode 100644 index 0000000..9656a77 --- /dev/null +++ b/tests/languages/java/snapshots/runnables_default_package.snap @@ -0,0 +1,49 @@ +--- +source: tests/support/mod.rs +assertion_line: 82 +expression: captures +--- +- name: _ + line: 1 + column: 1 + text: "public class DefaultPackage {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" +- name: class-modifier + line: 1 + column: 1 + text: public +- name: java_class_name + line: 1 + column: 14 + text: DefaultPackage +- name: modifier + line: 2 + column: 5 + text: public static +- name: run + line: 2 + column: 24 + text: main +- name: _ + line: 1 + column: 1 + text: "public class DefaultPackage {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" +- name: class-modifier + line: 1 + column: 1 + text: public +- name: java_class_name + line: 1 + column: 14 + text: DefaultPackage +- name: run + line: 1 + column: 14 + text: DefaultPackage +- name: modifier + line: 2 + column: 5 + text: public static +- name: method_name + line: 2 + column: 24 + text: main diff --git a/tests/languages/java/snapshots/runnables_default_package_test.snap b/tests/languages/java/snapshots/runnables_default_package_test.snap new file mode 100644 index 0000000..348a873 --- /dev/null +++ b/tests/languages/java/snapshots/runnables_default_package_test.snap @@ -0,0 +1,41 @@ +--- +source: tests/support/mod.rs +assertion_line: 82 +expression: captures +--- +- name: _ + line: 3 + column: 1 + text: "public class DefaultPackageTest {\n @Test\n void testMethod() {}\n}" +- name: java_class_name + line: 3 + column: 14 + text: DefaultPackageTest +- name: run + line: 3 + column: 14 + text: DefaultPackageTest +- name: annotation_name + line: 4 + column: 6 + text: Test +- name: _ + line: 3 + column: 1 + text: "public class DefaultPackageTest {\n @Test\n void testMethod() {}\n}" +- name: java_class_name + line: 3 + column: 14 + text: DefaultPackageTest +- name: annotation_name + line: 4 + column: 6 + text: Test +- name: run + line: 5 + column: 10 + text: testMethod +- name: java_method_name + line: 5 + column: 10 + text: testMethod diff --git a/tests/languages/java/snapshots/runnables_multi_level.snap b/tests/languages/java/snapshots/runnables_multi_level.snap index 31c955c..2c4e90b 100644 --- a/tests/languages/java/snapshots/runnables_multi_level.snap +++ b/tests/languages/java/snapshots/runnables_multi_level.snap @@ -1,6 +1,6 @@ --- source: tests/support/mod.rs -assertion_line: 126 +assertion_line: 82 expression: captures --- - name: java_package_name @@ -10,7 +10,7 @@ expression: captures - name: _ line: 3 column: 1 - text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" + text: "public class MultiLevelPackage {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" - name: class-modifier line: 3 column: 1 @@ -18,7 +18,7 @@ expression: captures - name: java_class_name line: 3 column: 14 - text: Main + text: MultiLevelPackage - name: modifier line: 4 column: 5 @@ -34,7 +34,7 @@ expression: captures - name: _ line: 3 column: 1 - text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" + text: "public class MultiLevelPackage {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" - name: class-modifier line: 3 column: 1 @@ -42,11 +42,11 @@ expression: captures - name: java_class_name line: 3 column: 14 - text: Main + text: MultiLevelPackage - name: run line: 3 column: 14 - text: Main + text: MultiLevelPackage - name: modifier line: 4 column: 5 diff --git a/tests/languages/java/snapshots/runnables_multi_level_test.snap b/tests/languages/java/snapshots/runnables_multi_level_test.snap new file mode 100644 index 0000000..837f5e3 --- /dev/null +++ b/tests/languages/java/snapshots/runnables_multi_level_test.snap @@ -0,0 +1,49 @@ +--- +source: tests/support/mod.rs +assertion_line: 82 +expression: captures +--- +- name: java_package_name + line: 1 + column: 9 + text: org.example +- name: _ + line: 5 + column: 1 + text: "public class MultiLevelTest {\n @Test\n void testMethod() {}\n}" +- name: java_class_name + line: 5 + column: 14 + text: MultiLevelTest +- name: run + line: 5 + column: 14 + text: MultiLevelTest +- name: annotation_name + line: 6 + column: 6 + text: Test +- name: java_package_name + line: 1 + column: 9 + text: org.example +- name: _ + line: 5 + column: 1 + text: "public class MultiLevelTest {\n @Test\n void testMethod() {}\n}" +- name: java_class_name + line: 5 + column: 14 + text: MultiLevelTest +- name: annotation_name + line: 6 + column: 6 + text: Test +- name: run + line: 7 + column: 10 + text: testMethod +- name: java_method_name + line: 7 + column: 10 + text: testMethod diff --git a/tests/languages/java/snapshots/runnables_single_level.snap b/tests/languages/java/snapshots/runnables_single_level.snap index 26550d8..3c00a67 100644 --- a/tests/languages/java/snapshots/runnables_single_level.snap +++ b/tests/languages/java/snapshots/runnables_single_level.snap @@ -1,6 +1,6 @@ --- source: tests/support/mod.rs -assertion_line: 126 +assertion_line: 82 expression: captures --- - name: java_package_name @@ -10,7 +10,7 @@ expression: captures - name: _ line: 3 column: 1 - text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" + text: "public class SingleLevelPackage {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" - name: class-modifier line: 3 column: 1 @@ -18,7 +18,7 @@ expression: captures - name: java_class_name line: 3 column: 14 - text: Main + text: SingleLevelPackage - name: modifier line: 4 column: 5 @@ -34,7 +34,7 @@ expression: captures - name: _ line: 3 column: 1 - text: "public class Main {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" + text: "public class SingleLevelPackage {\n public static void main(String[] args) {\n System.out.println(\"Hello world!\");\n }\n}" - name: class-modifier line: 3 column: 1 @@ -42,11 +42,11 @@ expression: captures - name: java_class_name line: 3 column: 14 - text: Main + text: SingleLevelPackage - name: run line: 3 column: 14 - text: Main + text: SingleLevelPackage - name: modifier line: 4 column: 5 diff --git a/tests/languages/java/snapshots/runnables_single_level_test.snap b/tests/languages/java/snapshots/runnables_single_level_test.snap new file mode 100644 index 0000000..eb95c05 --- /dev/null +++ b/tests/languages/java/snapshots/runnables_single_level_test.snap @@ -0,0 +1,49 @@ +--- +source: tests/support/mod.rs +assertion_line: 82 +expression: captures +--- +- name: java_package_name + line: 1 + column: 9 + text: example +- name: _ + line: 5 + column: 1 + text: "public class SingleLevelTest {\n @Test\n void testMethod() {}\n}" +- name: java_class_name + line: 5 + column: 14 + text: SingleLevelTest +- name: run + line: 5 + column: 14 + text: SingleLevelTest +- name: annotation_name + line: 6 + column: 6 + text: Test +- name: java_package_name + line: 1 + column: 9 + text: example +- name: _ + line: 5 + column: 1 + text: "public class SingleLevelTest {\n @Test\n void testMethod() {}\n}" +- name: java_class_name + line: 5 + column: 14 + text: SingleLevelTest +- name: annotation_name + line: 6 + column: 6 + text: Test +- name: run + line: 7 + column: 10 + text: testMethod +- name: java_method_name + line: 7 + column: 10 + text: testMethod diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index 6db9ddb..acd42dd 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -149,6 +149,10 @@ impl<'a> TaskRunner<'a> { self.zed_file = path; self } + fn package(mut self, p: &str) -> Self { + self.package = p.to_string(); + self + } fn class(mut self, c: &str) -> Self { self.class = c.to_string(); self @@ -358,6 +362,67 @@ fn test_maven_test_class_logic() { ); } +#[test] +fn test_maven_single_level_package_logic() { + let project = TestProject::new("maven_single_package", "maven", None); + let stdout = project + .task("java-main") + .package("example") + .class("Main") + .run(); + + assert!( + stdout.contains("-Dexec.mainClass=example.Main"), + "Should include the single-level package in Maven. Got: {}", + stdout + ); +} + +#[test] +fn test_maven_default_package_command_logic() { + let project = TestProject::new("maven_default_package", "maven", None); + let stdout = project.task("java-main").package("").class("Main").run(); + + assert!( + stdout.contains("-Dexec.mainClass=Main"), + "Should not include leading dot for default package in Maven. Got: {}", + stdout + ); +} + +#[test] +fn test_maven_default_package_test_method_logic() { + let project = TestProject::new("maven_default_test", "maven", None); + let stdout = project + .task("java-test-method") + .package("") + .class("MyTest") + .method("testMethod") + .run(); + + assert!( + stdout.contains("-Dtest=MyTest#testMethod"), + "Should not include leading dot for test method in default package. Got: {}", + stdout + ); +} + +#[test] +fn test_maven_default_package_test_class_logic() { + let project = TestProject::new("maven_default_class", "maven", None); + let stdout = project + .task("java-test-class") + .package("") + .class("MyTest") + .run(); + + assert!( + stdout.contains("-Dtest=MyTest"), + "Should not include leading dot for test class in default package. Got: {}", + stdout + ); +} + // --- Gradle Tests --- #[test] @@ -459,6 +524,72 @@ fn test_gradle_test_class_logic() { ); } +#[test] +fn test_gradle_single_level_package_logic() { + let project = TestProject::new("gradle_single_package", "gradle", None); + let stdout = project + .task("java-main") + .package("example") + .class("Main") + .run(); + + assert!( + stdout.contains("-PmainClass=example.Main"), + "Should include the single-level package in Gradle. Got: {}", + stdout + ); +} + +#[test] +fn test_gradle_default_package_command_logic() { + let project = TestProject::new("gradle_default_package", "gradle", None); + let stdout = project + .task("java-main") + .package("") + .class("Main") + .run(); + + assert!( + stdout.contains("-PmainClass=Main"), + "Should not include leading dot for default package in Gradle. Got: {}", + stdout + ); +} + +#[test] +fn test_gradle_default_package_test_method_logic() { + let project = TestProject::new("gradle_default_test", "gradle", None); + let stdout = project + .task("java-test-method") + .package("") + .class("MyTest") + .method("testMethod") + .run(); + + assert!( + stdout.contains("--tests MyTest.testMethod"), + "Should not include leading dot for test method in default package (Gradle). Got: {}", + stdout + ); +} + + +#[test] +fn test_gradle_default_package_test_class_logic() { + let project = TestProject::new("gradle_default_class", "gradle", None); + let stdout = project + .task("java-test-class") + .package("") + .class("MyTest") + .run(); + + assert!( + stdout.contains("--tests MyTest"), + "Should not include leading dot for test class in default package (Gradle). Got: {}", + stdout + ); +} + // --- Generic Tests --- #[test] From c2710a6d6842d0ce429a910ee22b37673dbb1907 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Mon, 18 May 2026 12:01:20 -0500 Subject: [PATCH 3/5] Format and lint all code --- languages/java/runnables.scm | 30 ++++++++++++++++++++++++------ tests/task_verification_test.rs | 7 +------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/languages/java/runnables.scm b/languages/java/runnables.scm index d3f9f07..a5a2857 100644 --- a/languages/java/runnables.scm +++ b/languages/java/runnables.scm @@ -1,7 +1,10 @@ ; Run the main function (program (package_declaration - [(identifier) (scoped_identifier)] @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration (modifiers) @class-modifier (#match? @class-modifier "public") @@ -18,7 +21,10 @@ ; Run the main class (program (package_declaration - [(identifier) (scoped_identifier)] @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration (modifiers) @class-modifier (#match? @class-modifier "public") @@ -35,7 +41,10 @@ ; Run test function (marker annotation, e.g. @Test) (program (package_declaration - [(identifier) (scoped_identifier)] @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration name: (identifier) @java_class_name body: (class_body @@ -54,7 +63,10 @@ ; Run nested test function (program (package_declaration - [(identifier) (scoped_identifier)] @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration name: (identifier) @java_outer_class_name body: (class_body @@ -80,7 +92,10 @@ ; Run test class (program (package_declaration - [(identifier) (scoped_identifier)] @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration name: (identifier) @java_class_name @run body: (class_body @@ -98,7 +113,10 @@ ; Run nested test class (program (package_declaration - [(identifier) (scoped_identifier)] @java_package_name)? + [ + (identifier) + (scoped_identifier) + ] @java_package_name)? (class_declaration name: (identifier) @java_outer_class_name body: (class_body diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index acd42dd..d940b58 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -543,11 +543,7 @@ fn test_gradle_single_level_package_logic() { #[test] fn test_gradle_default_package_command_logic() { let project = TestProject::new("gradle_default_package", "gradle", None); - let stdout = project - .task("java-main") - .package("") - .class("Main") - .run(); + let stdout = project.task("java-main").package("").class("Main").run(); assert!( stdout.contains("-PmainClass=Main"), @@ -573,7 +569,6 @@ fn test_gradle_default_package_test_method_logic() { ); } - #[test] fn test_gradle_default_package_test_class_logic() { let project = TestProject::new("gradle_default_class", "gradle", None); From bafd7ab8e100ad3c6e52abf70f3c5976804a0534 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Mon, 18 May 2026 13:01:26 -0500 Subject: [PATCH 4/5] Add nested test --- tests/java.rs | 9 +++ tests/languages/java/example/NestedTest.java | 12 ++++ .../java/snapshots/runnables_nested_test.snap | 65 +++++++++++++++++++ tests/task_verification_test.rs | 18 +++++ 4 files changed, 104 insertions(+) create mode 100644 tests/languages/java/example/NestedTest.java create mode 100644 tests/languages/java/snapshots/runnables_nested_test.snap diff --git a/tests/java.rs b/tests/java.rs index c01bd51..1ac71d0 100644 --- a/tests/java.rs +++ b/tests/java.rs @@ -61,3 +61,12 @@ fn runnables_default_package_test() { "languages/java/runnables.scm", ); } + +#[test] +fn runnables_nested_test() { + support::assert_query_snapshot( + "runnables_nested_test", + "tests/languages/java/example/NestedTest.java", + "languages/java/runnables.scm", + ); +} diff --git a/tests/languages/java/example/NestedTest.java b/tests/languages/java/example/NestedTest.java new file mode 100644 index 0000000..70a8d5a --- /dev/null +++ b/tests/languages/java/example/NestedTest.java @@ -0,0 +1,12 @@ +package example; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +public class NestedTest { + @Nested + class Inner { + @Test + void testMethod() {} + } +} diff --git a/tests/languages/java/snapshots/runnables_nested_test.snap b/tests/languages/java/snapshots/runnables_nested_test.snap new file mode 100644 index 0000000..389732a --- /dev/null +++ b/tests/languages/java/snapshots/runnables_nested_test.snap @@ -0,0 +1,65 @@ +--- +source: tests/support/mod.rs +assertion_line: 82 +expression: captures +--- +- name: java_package_name + line: 1 + column: 9 + text: example +- name: java_outer_class_name + line: 6 + column: 14 + text: NestedTest +- name: _ + line: 7 + column: 5 + text: "@Nested\n class Inner {\n @Test\n void testMethod() {}\n }" +- name: nested_annotation + line: 7 + column: 6 + text: Nested +- name: run + line: 8 + column: 11 + text: Inner +- name: java_class_name + line: 8 + column: 11 + text: Inner +- name: annotation_name + line: 9 + column: 10 + text: Test +- name: java_package_name + line: 1 + column: 9 + text: example +- name: java_outer_class_name + line: 6 + column: 14 + text: NestedTest +- name: _ + line: 7 + column: 5 + text: "@Nested\n class Inner {\n @Test\n void testMethod() {}\n }" +- name: nested_annotation + line: 7 + column: 6 + text: Nested +- name: java_class_name + line: 8 + column: 11 + text: Inner +- name: annotation_name + line: 9 + column: 10 + text: Test +- name: run + line: 10 + column: 14 + text: testMethod +- name: java_method_name + line: 10 + column: 14 + text: testMethod diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index d940b58..2050e24 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -585,6 +585,24 @@ fn test_gradle_default_package_test_class_logic() { ); } +#[test] +fn test_gradle_nested_test_logic() { + let project = TestProject::new("gradle_nested_test", "gradle", None); + let stdout = project + .task("java-test-method") + .package("example") + .outer_class("NestedTest") + .class("Inner") + .method("testMethod") + .run(); + + assert!( + stdout.contains("--tests example.NestedTest$Inner.testMethod"), + "Should correctly format nested test for Gradle. Got: {}", + stdout + ); +} + // --- Generic Tests --- #[test] From d4705fe1792d2f17e0e1016792253f00cd944679 Mon Sep 17 00:00:00 2001 From: Fredy Wijaya Date: Mon, 18 May 2026 13:19:10 -0500 Subject: [PATCH 5/5] Add nested test coverage --- tests/task_verification_test.rs | 54 ++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index 2050e24..20f1a40 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -423,6 +423,41 @@ fn test_maven_default_package_test_class_logic() { ); } +#[test] +fn test_maven_nested_test_method_logic() { + let project = TestProject::new("maven_nested_method", "maven", None); + let stdout = project + .task("java-test-method") + .package("example") + .outer_class("NestedTest") + .class("Inner") + .method("testMethod") + .run(); + + assert!( + stdout.contains("-Dtest=example.NestedTest$Inner#testMethod"), + "Should correctly format nested test method for Maven. Got: {}", + stdout + ); +} + +#[test] +fn test_maven_nested_test_class_logic() { + let project = TestProject::new("maven_nested_test_class", "maven", None); + let stdout = project + .task("java-test-class") + .package("example") + .outer_class("NestedTest") + .class("Inner") + .run(); + + assert!( + stdout.contains("-Dtest=example.NestedTest$Inner"), + "Should correctly format nested test class for Maven. Got: {}", + stdout + ); +} + // --- Gradle Tests --- #[test] @@ -586,7 +621,7 @@ fn test_gradle_default_package_test_class_logic() { } #[test] -fn test_gradle_nested_test_logic() { +fn test_gradle_nested_test_method_logic() { let project = TestProject::new("gradle_nested_test", "gradle", None); let stdout = project .task("java-test-method") @@ -603,6 +638,23 @@ fn test_gradle_nested_test_logic() { ); } +#[test] +fn test_gradle_nested_test_class_logic() { + let project = TestProject::new("gradle_nested_test_class", "gradle", None); + let stdout = project + .task("java-test-method") + .package("example") + .outer_class("NestedTest") + .class("Inner") + .run(); + + assert!( + stdout.contains("--tests example.NestedTest$Inner"), + "Should correctly format nested test for Gradle. Got: {}", + stdout + ); +} + // --- Generic Tests --- #[test]