rust: Add runnable icon for `#[cfg(test)] mod tests` (#12017)

Remco Smits created

This allows you to run all the tests inside the `mod tests` block. 
Fixes #11967.

<img width="366" alt="Screenshot 2024-05-18 at 16 07 32"
src="https://github.com/zed-industries/zed/assets/62463826/01fc378c-1546-421d-8250-fe0227c1e5a0">

<img width="874" alt="Screenshot 2024-05-18 at 16 37 21"
src="https://github.com/zed-industries/zed/assets/62463826/4a880b91-df84-4917-a16e-5d5fe20e22ac">

Release Notes:

- Added runnable icon for Rust `#[cfg(test)] mod tests` blocks
([#11967](https://github.com/zed-industries/zed/issues/11967)).

Change summary

crates/languages/src/rust.rs            | 16 ++++++++++++++++
crates/languages/src/rust/runnables.scm |  5 +++++
crates/project/src/task_inventory.rs    | 12 +++++++++++-
crates/task/src/lib.rs                  |  6 ++++++
crates/tasks_ui/src/lib.rs              |  6 ++++++
docs/src/tasks.md                       |  6 ++++--
6 files changed, 48 insertions(+), 3 deletions(-)

Detailed changes

crates/languages/src/rust.rs 🔗

@@ -389,6 +389,22 @@ impl ContextProvider for RustContextProvider {
                 tags: vec!["rust-test".to_owned()],
                 ..TaskTemplate::default()
             },
+            TaskTemplate {
+                label: format!(
+                    "cargo test -p {} {}",
+                    RUST_PACKAGE_TASK_VARIABLE.template_value(),
+                    VariableName::Stem.template_value(),
+                ),
+                command: "cargo".into(),
+                args: vec![
+                    "test".into(),
+                    "-p".into(),
+                    RUST_PACKAGE_TASK_VARIABLE.template_value(),
+                    VariableName::Stem.template_value(),
+                ],
+                tags: vec!["rust-mod-test".to_owned()],
+                ..TaskTemplate::default()
+            },
             TaskTemplate {
                 label: format!(
                     "cargo test -p {}",

crates/project/src/task_inventory.rs 🔗

@@ -550,7 +550,17 @@ impl ContextProvider for BasicContextProvider {
             task_variables.insert(VariableName::SelectedText, selected_text);
         }
         if let Some(path) = current_file {
-            task_variables.insert(VariableName::File, path);
+            task_variables.insert(VariableName::File, path.clone());
+
+            let path = Path::new(&path);
+
+            if let Some(filename) = path.file_name().and_then(|f| f.to_str()) {
+                task_variables.insert(VariableName::Filename, String::from(filename));
+            }
+
+            if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
+                task_variables.insert(VariableName::Stem, String::from(stem));
+            }
         }
 
         let worktree_abs_path = buffer

crates/task/src/lib.rs 🔗

@@ -124,6 +124,10 @@ impl ResolvedTask {
 pub enum VariableName {
     /// An absolute path of the currently opened file.
     File,
+    /// the currently opened filename.
+    Filename,
+    /// stem (filename without extension) of the currently opened file.
+    Stem,
     /// An absolute path of the currently opened worktree, that contains the file.
     WorktreeRoot,
     /// A symbol text, that contains latest cursor/selection position.
@@ -160,6 +164,8 @@ impl std::fmt::Display for VariableName {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         match self {
             Self::File => write!(f, "{ZED_VARIABLE_NAME_PREFIX}FILE"),
+            Self::Filename => write!(f, "{ZED_VARIABLE_NAME_PREFIX}FILENAME"),
+            Self::Stem => write!(f, "{ZED_VARIABLE_NAME_PREFIX}STEM"),
             Self::WorktreeRoot => write!(f, "{ZED_VARIABLE_NAME_PREFIX}WORKTREE_ROOT"),
             Self::Symbol => write!(f, "{ZED_VARIABLE_NAME_PREFIX}SYMBOL"),
             Self::Row => write!(f, "{ZED_VARIABLE_NAME_PREFIX}ROW"),

crates/tasks_ui/src/lib.rs 🔗

@@ -260,6 +260,8 @@ mod tests {
                     cwd: Some("/dir".into()),
                     task_variables: TaskVariables::from_iter([
                         (VariableName::File, "/dir/rust/b.rs".into()),
+                        (VariableName::Filename, "b.rs".into()),
+                        (VariableName::Stem, "b".into()),
                         (VariableName::WorktreeRoot, "/dir".into()),
                         (VariableName::Row, "1".into()),
                         (VariableName::Column, "1".into()),
@@ -276,6 +278,8 @@ mod tests {
                     cwd: Some("/dir".into()),
                     task_variables: TaskVariables::from_iter([
                         (VariableName::File, "/dir/rust/b.rs".into()),
+                        (VariableName::Filename, "b.rs".into()),
+                        (VariableName::Stem, "b".into()),
                         (VariableName::WorktreeRoot, "/dir".into()),
                         (VariableName::Row, "1".into()),
                         (VariableName::Column, "15".into()),
@@ -293,6 +297,8 @@ mod tests {
                     cwd: Some("/dir".into()),
                     task_variables: TaskVariables::from_iter([
                         (VariableName::File, "/dir/a.ts".into()),
+                        (VariableName::Filename, "a.ts".into()),
+                        (VariableName::Stem, "a".into()),
                         (VariableName::WorktreeRoot, "/dir".into()),
                         (VariableName::Row, "1".into()),
                         (VariableName::Column, "1".into()),

docs/src/tasks.md 🔗

@@ -43,10 +43,12 @@ These variables allow you to pull information from the current editor and use it
 
 - `ZED_COLUMN`: current line column
 - `ZED_ROW`: current line row
-- `ZED_FILE`: absolute path to the file
+- `ZED_FILE`: absolute path of the currently opened file (e.g. `/Users/my-user/path/to/project/src/main.rs`)
+- `ZED_FILENAME`: filename of the currently opened file (e.g. `main.rs`)
+- `ZED_STEM`: stem (filename without extension) of the currently opened file (e.g. `main`)
 - `ZED_SYMBOL`: currently selected symbol; should match the last symbol shown in a symbol breadcrumb (e.g. `mod tests > fn test_task_contexts`)
 - `ZED_SELECTED_TEXT`: currently selected text
-- `ZED_WORKTREE_ROOT`: absolute path to the root of the current worktree.
+- `ZED_WORKTREE_ROOT`: absolute path to the root of the current worktree. (e.g. `/Users/my-user/path/to/project`)
 - `ZED_CUSTOM_RUST_PACKAGE`: (Rust-specific) name of the parent package of $ZED_FILE source file.
 
 To use a variable in a task, prefix it with a dollar sign (`$`):