diff --git a/Cargo.lock b/Cargo.lock index aaa8cbb669..81510a6ad3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7695,6 +7695,7 @@ dependencies = [ "lsp", "node_runtime", "parking_lot", + "pathdiff", "postage", "prettier", "pretty_assertions", diff --git a/Cargo.toml b/Cargo.toml index 993c66e563..b174f80fbb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -305,6 +305,7 @@ once_cell = "1.19.0" ordered-float = "2.1.1" palette = { version = "0.7.5", default-features = false, features = ["std"] } parking_lot = "0.12.1" +pathdiff = "0.2" profiling = "1" postage = { version = "0.5", features = ["futures-traits"] } pretty_assertions = "1.3.0" diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index ae5c510f35..a35d2f73c4 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -44,6 +44,7 @@ log.workspace = true lsp.workspace = true node_runtime.workspace = true parking_lot.workspace = true +pathdiff.workspace = true postage.workspace = true prettier.workspace = true worktree.workspace = true diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index fbed35e382..bc5d9fdfab 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -549,20 +549,6 @@ impl ContextProvider for BasicContextProvider { if !selected_text.trim().is_empty() { task_variables.insert(VariableName::SelectedText, selected_text); } - if let Some(path) = current_file { - 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 .file() .map(|file| WorktreeId::from_usize(file.worktree_id())) @@ -577,6 +563,32 @@ impl ContextProvider for BasicContextProvider { VariableName::WorktreeRoot, worktree_path.to_string_lossy().to_string(), ); + if let Some(full_path) = current_file.as_ref() { + let relative_path = pathdiff::diff_paths(full_path, worktree_path); + if let Some(relative_path) = relative_path { + task_variables.insert( + VariableName::RelativeFile, + relative_path.to_string_lossy().into_owned(), + ); + } + } + } + + if let Some(path_as_string) = current_file { + let path = Path::new(&path_as_string); + 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, stem.into()); + } + + if let Some(dirname) = path.parent().and_then(|s| s.to_str()) { + task_variables.insert(VariableName::Dirname, dirname.into()); + } + + task_variables.insert(VariableName::File, path_as_string); } Ok(task_variables) diff --git a/crates/task/src/lib.rs b/crates/task/src/lib.rs index 3d7a4e3a5b..91f3cdb1f9 100644 --- a/crates/task/src/lib.rs +++ b/crates/task/src/lib.rs @@ -124,9 +124,13 @@ impl ResolvedTask { pub enum VariableName { /// An absolute path of the currently opened file. File, - /// the currently opened filename. + /// A path of the currently opened file (relative to worktree root). + RelativeFile, + /// The currently opened filename. Filename, - /// stem (filename without extension) of the currently opened file. + /// The path to a parent directory of a currently opened file. + Dirname, + /// Stem (filename without extension) of the currently opened file. Stem, /// An absolute path of the currently opened worktree, that contains the file. WorktreeRoot, @@ -165,6 +169,8 @@ impl std::fmt::Display for VariableName { match self { Self::File => write!(f, "{ZED_VARIABLE_NAME_PREFIX}FILE"), Self::Filename => write!(f, "{ZED_VARIABLE_NAME_PREFIX}FILENAME"), + Self::RelativeFile => write!(f, "{ZED_VARIABLE_NAME_PREFIX}RELATIVE_FILE"), + Self::Dirname => write!(f, "{ZED_VARIABLE_NAME_PREFIX}DIRNAME"), 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"), diff --git a/crates/tasks_ui/src/lib.rs b/crates/tasks_ui/src/lib.rs index bbc3161cd7..051a59b46b 100644 --- a/crates/tasks_ui/src/lib.rs +++ b/crates/tasks_ui/src/lib.rs @@ -261,6 +261,8 @@ mod tests { task_variables: TaskVariables::from_iter([ (VariableName::File, "/dir/rust/b.rs".into()), (VariableName::Filename, "b.rs".into()), + (VariableName::RelativeFile, "rust/b.rs".into()), + (VariableName::Dirname, "/dir/rust".into()), (VariableName::Stem, "b".into()), (VariableName::WorktreeRoot, "/dir".into()), (VariableName::Row, "1".into()), @@ -279,6 +281,8 @@ mod tests { task_variables: TaskVariables::from_iter([ (VariableName::File, "/dir/rust/b.rs".into()), (VariableName::Filename, "b.rs".into()), + (VariableName::RelativeFile, "rust/b.rs".into()), + (VariableName::Dirname, "/dir/rust".into()), (VariableName::Stem, "b".into()), (VariableName::WorktreeRoot, "/dir".into()), (VariableName::Row, "1".into()), @@ -298,6 +302,8 @@ mod tests { task_variables: TaskVariables::from_iter([ (VariableName::File, "/dir/a.ts".into()), (VariableName::Filename, "a.ts".into()), + (VariableName::RelativeFile, "a.ts".into()), + (VariableName::Dirname, "/dir".into()), (VariableName::Stem, "a".into()), (VariableName::WorktreeRoot, "/dir".into()), (VariableName::Row, "1".into()), diff --git a/docs/src/tasks.md b/docs/src/tasks.md index a89f5e9436..06687acf5a 100644 --- a/docs/src/tasks.md +++ b/docs/src/tasks.md @@ -45,6 +45,8 @@ These variables allow you to pull information from the current editor and use it - `ZED_ROW`: current line row - `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_DIRNAME`: absolute path of the currently opened file with file name stripped (e.g. `/Users/my-user/path/to/project/src`) +- `ZED_RELATIVE_FILE`: path of the currently opened file, relative to `ZED_WORKTREE_ROOT` (e.g. `src/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