debugger: Run debug scenarios from package.json (#32958)

Release Notes:

- New session modal for a debugger will now show tasks from package.json
as debuggable scenarios

---------

Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
This commit is contained in:
Piotr Osiewicz 2025-06-18 20:04:25 +02:00 committed by GitHub
parent 73fee01c85
commit 99215f7660
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 83 additions and 25 deletions

View file

@ -5,12 +5,14 @@ use async_trait::async_trait;
use collections::HashMap;
use dap::DapRegistry;
use futures::StreamExt;
use gpui::{App, AsyncApp};
use gpui::{App, AsyncApp, Task};
use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
use language::{LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
use language::{
ContextProvider, LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate,
};
use lsp::{LanguageServerBinary, LanguageServerName};
use node_runtime::NodeRuntime;
use project::{ContextProviderWithTasks, Fs, lsp_store::language_server_settings};
use project::{Fs, lsp_store::language_server_settings};
use serde_json::{Value, json};
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
use smol::{
@ -36,25 +38,77 @@ const SERVER_PATH: &str =
const TSCONFIG_SCHEMA: &str = include_str!("json/schemas/tsconfig.json");
const PACKAGE_JSON_SCHEMA: &str = include_str!("json/schemas/package.json");
pub(super) fn json_task_context() -> ContextProviderWithTasks {
ContextProviderWithTasks::new(TaskTemplates(vec![
TaskTemplate {
label: "package script $ZED_CUSTOM_script".to_owned(),
command: "npm --prefix $ZED_DIRNAME run".to_owned(),
args: vec![VariableName::Custom("script".into()).template_value()],
tags: vec!["package-script".into()],
..TaskTemplate::default()
},
TaskTemplate {
label: "composer script $ZED_CUSTOM_script".to_owned(),
command: "composer -d $ZED_DIRNAME".to_owned(),
args: vec![VariableName::Custom("script".into()).template_value()],
tags: vec!["composer-script".into()],
..TaskTemplate::default()
},
]))
}
pub(crate) struct JsonTaskProvider;
impl ContextProvider for JsonTaskProvider {
fn associated_tasks(
&self,
_: Arc<dyn Fs>,
file: Option<Arc<dyn language::File>>,
cx: &App,
) -> gpui::Task<Option<TaskTemplates>> {
let Some(file) = project::File::from_dyn(file.as_ref())
.filter(|file| file.path.file_name() == Some("package.json".as_ref()))
.cloned()
else {
return Task::ready(None);
};
cx.spawn(async move |cx| {
let contents = file
.worktree
.update(cx, |this, cx| this.load_file(&file.path, cx))
.ok()?
.await
.ok()?;
let as_json = serde_json_lenient::Value::from_str(&contents.text).ok()?;
let gutter_tasks = [
TaskTemplate {
label: "package script $ZED_CUSTOM_script".to_owned(),
command: "npm".to_owned(),
args: vec![
"--prefix".into(),
"$ZED_DIRNAME".into(),
"run".into(),
VariableName::Custom("script".into()).template_value(),
],
tags: vec!["package-script".into()],
..TaskTemplate::default()
},
TaskTemplate {
label: "composer script $ZED_CUSTOM_script".to_owned(),
command: "composer".to_owned(),
args: vec![
"-d".into(),
"$ZED_DIRNAME".into(),
VariableName::Custom("script".into()).template_value(),
],
tags: vec!["composer-script".into()],
..TaskTemplate::default()
},
];
let tasks = as_json
.get("scripts")?
.as_object()?
.keys()
.map(|key| TaskTemplate {
label: format!("run {key}"),
command: "npm".to_owned(),
args: vec![
"--prefix".into(),
"$ZED_DIRNAME".into(),
"run".into(),
key.into(),
],
..TaskTemplate::default()
})
.chain(gutter_tasks)
.collect();
Some(TaskTemplates(tasks))
})
}
}
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
vec![server_path.into(), "--stdio".into()]
}

View file

@ -10,6 +10,6 @@ brackets = [
]
tab_size = 2
prettier_parser_name = "json"
debuggers = ["JavaScript"]
[overrides.string]
completion_query_characters = [":", " "]

View file

@ -1,6 +1,5 @@
use anyhow::Context as _;
use gpui::{App, UpdateGlobal};
use json::json_task_context;
use node_runtime::NodeRuntime;
use python::PyprojectTomlManifestProvider;
use rust::CargoManifestProvider;
@ -12,6 +11,8 @@ use util::{ResultExt, asset_str};
pub use language::*;
use crate::json::JsonTaskProvider;
mod bash;
mod c;
mod css;
@ -78,7 +79,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node: NodeRuntime, cx: &mut App) {
let eslint_adapter = Arc::new(typescript::EsLintLspAdapter::new(node.clone()));
let go_context_provider = Arc::new(go::GoContextProvider);
let go_lsp_adapter = Arc::new(go::GoLspAdapter);
let json_context_provider = Arc::new(json_task_context());
let json_context_provider = Arc::new(JsonTaskProvider);
let json_lsp_adapter = Arc::new(json::JsonLspAdapter::new(node.clone(), languages.clone()));
let node_version_lsp_adapter = Arc::new(json::NodeVersionAdapter);
let py_lsp_adapter = Arc::new(python::PyLspAdapter::new());

View file

@ -28,7 +28,10 @@ impl DapLocator for NodeLocator {
if adapter.0.as_ref() != "JavaScript" {
return None;
}
if build_config.command != TYPESCRIPT_RUNNER_VARIABLE.template_value() {
if build_config.command != TYPESCRIPT_RUNNER_VARIABLE.template_value()
&& build_config.command != "composer"
&& build_config.command != "npm"
{
return None;
}