From e224da852250d11a77fb6e757d3db306de91ed9c Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Wed, 2 Jul 2025 16:37:36 -0400 Subject: [PATCH] Disambiguate package.json tasks by parent directory as needed (#33798) Closes #33701, cc @afgomez Release Notes: - Added the parent directory to the label as needed to disambiguate tasks from package.json --- crates/languages/src/json.rs | 6 ++-- crates/languages/src/typescript.rs | 57 ++++++++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index bd950f34f5..6f51cdadba 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -8,7 +8,8 @@ use futures::StreamExt; use gpui::{App, AsyncApp, Task}; use http_client::github::{GitHubLspBinaryVersion, latest_github_release}; use language::{ - ContextProvider, LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate, + ContextProvider, LanguageRegistry, LanguageToolchainStore, LocalFile as _, LspAdapter, + LspAdapterDelegate, }; use lsp::{LanguageServerBinary, LanguageServerName}; use node_runtime::NodeRuntime; @@ -65,13 +66,14 @@ impl ContextProvider for JsonTaskProvider { .ok()? .await .ok()?; + let path = cx.update(|cx| file.abs_path(cx)).ok()?.as_path().into(); let task_templates = if is_package_json { let package_json = serde_json_lenient::from_str::< HashMap, >(&contents.text) .ok()?; - let package_json = PackageJsonData::new(file.path.clone(), package_json); + let package_json = PackageJsonData::new(path, package_json); let command = package_json.package_manager.unwrap_or("npm").to_owned(); package_json .scripts diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index 4a9626c8b8..32c45dfa88 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -221,15 +221,30 @@ impl PackageJsonData { }); } + let script_name_counts: HashMap<_, usize> = + self.scripts + .iter() + .fold(HashMap::default(), |mut acc, (_, script)| { + *acc.entry(script).or_default() += 1; + acc + }); for (path, script) in &self.scripts { + let label = if script_name_counts.get(script).copied().unwrap_or_default() > 1 + && let Some(parent) = path.parent().and_then(|parent| parent.file_name()) + { + let parent = parent.to_string_lossy(); + format!("{parent}/package.json > {script}") + } else { + format!("package.json > {script}") + }; task_templates.0.push(TaskTemplate { - label: format!("package.json > {script}",), + label, command: TYPESCRIPT_RUNNER_VARIABLE.template_value(), args: vec!["run".to_owned(), script.to_owned()], tags: vec!["package-script".into()], cwd: Some( path.parent() - .unwrap_or(Path::new("")) + .unwrap_or(Path::new("/")) .to_string_lossy() .to_string(), ), @@ -1014,6 +1029,7 @@ mod tests { use language::language_settings; use project::{FakeFs, Project}; use serde_json::json; + use task::TaskTemplates; use unindent::Unindent; use util::path; @@ -1135,5 +1151,42 @@ mod tests { package_manager: None, } ); + + let mut task_templates = TaskTemplates::default(); + package_json_data.fill_task_templates(&mut task_templates); + let task_templates = task_templates + .0 + .into_iter() + .map(|template| (template.label, template.cwd)) + .collect::>(); + pretty_assertions::assert_eq!( + task_templates, + [ + ( + "vitest file test".into(), + Some("$ZED_CUSTOM_TYPESCRIPT_VITEST_PACKAGE_PATH".into()), + ), + ( + "vitest test $ZED_SYMBOL".into(), + Some("$ZED_CUSTOM_TYPESCRIPT_VITEST_PACKAGE_PATH".into()), + ), + ( + "mocha file test".into(), + Some("$ZED_CUSTOM_TYPESCRIPT_MOCHA_PACKAGE_PATH".into()), + ), + ( + "mocha test $ZED_SYMBOL".into(), + Some("$ZED_CUSTOM_TYPESCRIPT_MOCHA_PACKAGE_PATH".into()), + ), + ( + "root/package.json > test".into(), + Some(path!("/root").into()) + ), + ( + "sub/package.json > test".into(), + Some(path!("/root/sub").into()) + ), + ] + ); } }