Set up Rust debugger code runner tasks (#27571)
## Summary This PR starts the process of adding debug task locators to Zed's debugger system. A task locator is a secondary resolution phase that allows a debug task to run a command before starting a debug session and then uses the output of the run command to configure itself. Locators are most applicable when debugging a compiled language but will be helpful for any language as well. ## Architecture At a high level, this works by adding a debug task queue to `Workspace`. Which add's a debug configuration associated with a `TaskId` whenever a resolved task with a debug config is added to `TaskInventory`'s queue. Then, when the `SpawnInTerminal` task finishes running, it emits its task_id and the result of the ran task. When a ran task exits successfully, `Workspace` tells `Project` to start a debug session using its stored debug config, then `DapStore` queries the `LocatorStore` to configure the debug configuration if it has a valid locator argument. Release Notes: - N/A
This commit is contained in:
parent
141a6c3915
commit
8add90d7cb
24 changed files with 441 additions and 168 deletions
|
@ -1,4 +1,3 @@
|
||||||
use anyhow::bail;
|
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
use std::{ffi::OsStr, path::PathBuf};
|
use std::{ffi::OsStr, path::PathBuf};
|
||||||
use task::DebugTaskDefinition;
|
use task::DebugTaskDefinition;
|
||||||
|
@ -63,9 +62,7 @@ impl DebugAdapter for GoDebugAdapter {
|
||||||
.and_then(|p| p.to_str().map(|p| p.to_string()))
|
.and_then(|p| p.to_str().map(|p| p.to_string()))
|
||||||
.ok_or(anyhow!("Dlv not found in path"))?;
|
.ok_or(anyhow!("Dlv not found in path"))?;
|
||||||
|
|
||||||
let Some(tcp_connection) = config.tcp_connection.clone() else {
|
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
|
||||||
bail!("Go Debug Adapter expects tcp connection arguments to be provided");
|
|
||||||
};
|
|
||||||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
|
|
|
@ -78,11 +78,7 @@ impl DebugAdapter for JsDebugAdapter {
|
||||||
.ok_or_else(|| anyhow!("Couldn't find JavaScript dap directory"))?
|
.ok_or_else(|| anyhow!("Couldn't find JavaScript dap directory"))?
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(tcp_connection) = config.tcp_connection.clone() else {
|
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
|
||||||
anyhow::bail!(
|
|
||||||
"Javascript Debug Adapter expects tcp connection arguments to be provided"
|
|
||||||
);
|
|
||||||
};
|
|
||||||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use adapters::latest_github_release;
|
use adapters::latest_github_release;
|
||||||
use anyhow::bail;
|
|
||||||
use dap::adapters::TcpArguments;
|
use dap::adapters::TcpArguments;
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -69,9 +68,7 @@ impl DebugAdapter for PhpDebugAdapter {
|
||||||
.ok_or_else(|| anyhow!("Couldn't find PHP dap directory"))?
|
.ok_or_else(|| anyhow!("Couldn't find PHP dap directory"))?
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(tcp_connection) = config.tcp_connection.clone() else {
|
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
|
||||||
bail!("PHP Debug Adapter expects tcp connection arguments to be provided");
|
|
||||||
};
|
|
||||||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use anyhow::bail;
|
|
||||||
use dap::DebugRequestType;
|
use dap::DebugRequestType;
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
use std::{ffi::OsStr, path::PathBuf};
|
use std::{ffi::OsStr, path::PathBuf};
|
||||||
|
@ -70,9 +69,7 @@ impl DebugAdapter for PythonDebugAdapter {
|
||||||
cx: &mut AsyncApp,
|
cx: &mut AsyncApp,
|
||||||
) -> Result<DebugAdapterBinary> {
|
) -> Result<DebugAdapterBinary> {
|
||||||
const BINARY_NAMES: [&str; 3] = ["python3", "python", "py"];
|
const BINARY_NAMES: [&str; 3] = ["python3", "python", "py"];
|
||||||
let Some(tcp_connection) = config.tcp_connection.clone() else {
|
let tcp_connection = config.tcp_connection.clone().unwrap_or_default();
|
||||||
bail!("Python Debug Adapter expects tcp connection arguments to be provided");
|
|
||||||
};
|
|
||||||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||||
|
|
||||||
let debugpy_dir = if let Some(user_installed_path) = user_installed_path {
|
let debugpy_dir = if let Some(user_installed_path) = user_installed_path {
|
||||||
|
|
|
@ -159,6 +159,8 @@ impl Render for InertState {
|
||||||
}),
|
}),
|
||||||
tcp_connection: Some(TCPHost::default()),
|
tcp_connection: Some(TCPHost::default()),
|
||||||
initialize_args: None,
|
initialize_args: None,
|
||||||
|
args: Default::default(),
|
||||||
|
locator: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -319,6 +321,8 @@ impl InertState {
|
||||||
adapter: kind,
|
adapter: kind,
|
||||||
request: DebugRequestType::Attach(task::AttachConfig { process_id: None }),
|
request: DebugRequestType::Attach(task::AttachConfig { process_id: None }),
|
||||||
initialize_args: None,
|
initialize_args: None,
|
||||||
|
args: Default::default(),
|
||||||
|
locator: None,
|
||||||
tcp_connection: Some(TCPHost::default()),
|
tcp_connection: Some(TCPHost::default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,8 @@ async fn test_show_attach_modal_and_select_process(
|
||||||
label: "attach example".into(),
|
label: "attach example".into(),
|
||||||
initialize_args: None,
|
initialize_args: None,
|
||||||
tcp_connection: Some(TCPHost::default()),
|
tcp_connection: Some(TCPHost::default()),
|
||||||
|
locator: None,
|
||||||
|
args: Default::default(),
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
Candidate {
|
Candidate {
|
||||||
|
|
|
@ -4850,6 +4850,7 @@ impl Editor {
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
|
let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
|
||||||
let action = actions_menu.actions.get(action_ix)?;
|
let action = actions_menu.actions.get(action_ix)?;
|
||||||
let title = action.label();
|
let title = action.label();
|
||||||
|
@ -4858,17 +4859,48 @@ impl Editor {
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
CodeActionsItem::Task(task_source_kind, resolved_task) => {
|
CodeActionsItem::Task(task_source_kind, resolved_task) => {
|
||||||
workspace.update(cx, |workspace, cx| {
|
match resolved_task.task_type() {
|
||||||
workspace::tasks::schedule_resolved_task(
|
task::TaskType::Script => workspace.update(cx, |workspace, cx| {
|
||||||
workspace,
|
workspace::tasks::schedule_resolved_task(
|
||||||
task_source_kind,
|
workspace,
|
||||||
resolved_task,
|
task_source_kind,
|
||||||
false,
|
resolved_task,
|
||||||
cx,
|
false,
|
||||||
);
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
Some(Task::ready(Ok(())))
|
Some(Task::ready(Ok(())))
|
||||||
})
|
}),
|
||||||
|
task::TaskType::Debug(debug_args) => {
|
||||||
|
if debug_args.locator.is_some() {
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
workspace::tasks::schedule_resolved_task(
|
||||||
|
workspace,
|
||||||
|
task_source_kind,
|
||||||
|
resolved_task,
|
||||||
|
false,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Some(Task::ready(Ok(())));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(project) = self.project.as_ref() {
|
||||||
|
project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.start_debug_session(
|
||||||
|
resolved_task.resolved_debug_adapter_config().unwrap(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
Some(Task::ready(Ok(())))
|
||||||
|
} else {
|
||||||
|
Some(Task::ready(Ok(())))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CodeActionsItem::CodeAction {
|
CodeActionsItem::CodeAction {
|
||||||
excerpt_id,
|
excerpt_id,
|
||||||
|
@ -8600,7 +8632,7 @@ impl Editor {
|
||||||
let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
|
let line_len = snapshot.buffer_snapshot.line_len(MultiBufferRow(row));
|
||||||
let anchor_end = snapshot
|
let anchor_end = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.anchor_before(Point::new(row, line_len));
|
.anchor_after(Point::new(row, line_len));
|
||||||
|
|
||||||
let bp = self
|
let bp = self
|
||||||
.breakpoint_store
|
.breakpoint_store
|
||||||
|
|
|
@ -17,7 +17,7 @@ use std::{
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{Arc, LazyLock},
|
sync::{Arc, LazyLock},
|
||||||
};
|
};
|
||||||
use task::{TaskTemplate, TaskTemplates, TaskVariables, VariableName};
|
use task::{TaskTemplate, TaskTemplates, TaskType, TaskVariables, VariableName};
|
||||||
use util::{fs::remove_matching, maybe, ResultExt};
|
use util::{fs::remove_matching, maybe, ResultExt};
|
||||||
|
|
||||||
use crate::language_settings::language_settings;
|
use crate::language_settings::language_settings;
|
||||||
|
@ -574,11 +574,16 @@ impl ContextProvider for RustContextProvider {
|
||||||
.variables
|
.variables
|
||||||
.get(CUSTOM_TARGET_DIR)
|
.get(CUSTOM_TARGET_DIR)
|
||||||
.cloned();
|
.cloned();
|
||||||
let run_task_args = if let Some(package_to_run) = package_to_run {
|
let run_task_args = if let Some(package_to_run) = package_to_run.clone() {
|
||||||
vec!["run".into(), "-p".into(), package_to_run]
|
vec!["run".into(), "-p".into(), package_to_run]
|
||||||
} else {
|
} else {
|
||||||
vec!["run".into()]
|
vec!["run".into()]
|
||||||
};
|
};
|
||||||
|
let debug_task_args = if let Some(package_to_run) = package_to_run {
|
||||||
|
vec!["build".into(), "-p".into(), package_to_run]
|
||||||
|
} else {
|
||||||
|
vec!["build".into()]
|
||||||
|
};
|
||||||
let mut task_templates = vec![
|
let mut task_templates = vec![
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: format!(
|
label: format!(
|
||||||
|
@ -620,6 +625,31 @@ impl ContextProvider for RustContextProvider {
|
||||||
cwd: Some("$ZED_DIRNAME".to_owned()),
|
cwd: Some("$ZED_DIRNAME".to_owned()),
|
||||||
..TaskTemplate::default()
|
..TaskTemplate::default()
|
||||||
},
|
},
|
||||||
|
TaskTemplate {
|
||||||
|
label: format!(
|
||||||
|
"Debug Test '{}' (package: {})",
|
||||||
|
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
|
||||||
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
),
|
||||||
|
task_type: TaskType::Debug(task::DebugArgs {
|
||||||
|
adapter: "LLDB".to_owned(),
|
||||||
|
request: task::DebugArgsRequest::Launch,
|
||||||
|
locator: Some("cargo".into()),
|
||||||
|
tcp_connection: None,
|
||||||
|
initialize_args: None,
|
||||||
|
}),
|
||||||
|
command: "cargo".into(),
|
||||||
|
args: vec![
|
||||||
|
"test".into(),
|
||||||
|
"-p".into(),
|
||||||
|
RUST_PACKAGE_TASK_VARIABLE.template_value(),
|
||||||
|
RUST_TEST_NAME_TASK_VARIABLE.template_value(),
|
||||||
|
"--no-run".into(),
|
||||||
|
],
|
||||||
|
tags: vec!["rust-test".to_owned()],
|
||||||
|
cwd: Some("$ZED_DIRNAME".to_owned()),
|
||||||
|
..TaskTemplate::default()
|
||||||
|
},
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: format!(
|
label: format!(
|
||||||
"Doc test '{}' (package: {})",
|
"Doc test '{}' (package: {})",
|
||||||
|
@ -697,6 +727,21 @@ impl ContextProvider for RustContextProvider {
|
||||||
cwd: Some("$ZED_DIRNAME".to_owned()),
|
cwd: Some("$ZED_DIRNAME".to_owned()),
|
||||||
..TaskTemplate::default()
|
..TaskTemplate::default()
|
||||||
},
|
},
|
||||||
|
TaskTemplate {
|
||||||
|
label: "Debug".into(),
|
||||||
|
cwd: Some("$ZED_DIRNAME".to_owned()),
|
||||||
|
command: "cargo".into(),
|
||||||
|
task_type: TaskType::Debug(task::DebugArgs {
|
||||||
|
request: task::DebugArgsRequest::Launch,
|
||||||
|
adapter: "LLDB".to_owned(),
|
||||||
|
initialize_args: None,
|
||||||
|
locator: Some("cargo".into()),
|
||||||
|
tcp_connection: None,
|
||||||
|
}),
|
||||||
|
args: debug_task_args,
|
||||||
|
tags: vec!["rust-main".to_owned()],
|
||||||
|
..TaskTemplate::default()
|
||||||
|
},
|
||||||
TaskTemplate {
|
TaskTemplate {
|
||||||
label: "Clean".into(),
|
label: "Clean".into(),
|
||||||
command: "cargo".into(),
|
command: "cargo".into(),
|
||||||
|
|
|
@ -14,4 +14,5 @@
|
||||||
pub mod breakpoint_store;
|
pub mod breakpoint_store;
|
||||||
pub mod dap_command;
|
pub mod dap_command;
|
||||||
pub mod dap_store;
|
pub mod dap_store;
|
||||||
|
mod locator_store;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
|
|
|
@ -12,7 +12,7 @@ use rpc::{
|
||||||
AnyProtoClient, TypedEnvelope,
|
AnyProtoClient, TypedEnvelope,
|
||||||
};
|
};
|
||||||
use std::{hash::Hash, ops::Range, path::Path, sync::Arc};
|
use std::{hash::Hash, ops::Range, path::Path, sync::Arc};
|
||||||
use text::{Point, PointUtf16};
|
use text::PointUtf16;
|
||||||
|
|
||||||
use crate::{buffer_store::BufferStore, worktree_store::WorktreeStore, Project, ProjectPath};
|
use crate::{buffer_store::BufferStore, worktree_store::WorktreeStore, Project, ProjectPath};
|
||||||
|
|
||||||
|
@ -494,7 +494,7 @@ impl BreakpointStore {
|
||||||
this.update(cx, |_, cx| BreakpointsInFile::new(buffer, cx))?;
|
this.update(cx, |_, cx| BreakpointsInFile::new(buffer, cx))?;
|
||||||
|
|
||||||
for bp in bps {
|
for bp in bps {
|
||||||
let position = snapshot.anchor_after(Point::new(bp.row, 0));
|
let position = snapshot.anchor_after(PointUtf16::new(bp.row, 0));
|
||||||
breakpoints_for_file.breakpoints.push((
|
breakpoints_for_file.breakpoints.push((
|
||||||
position,
|
position,
|
||||||
Breakpoint {
|
Breakpoint {
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
use super::{
|
use super::{
|
||||||
breakpoint_store::BreakpointStore,
|
breakpoint_store::BreakpointStore,
|
||||||
// Will need to uncomment this once we implement rpc message handler again
|
locator_store::LocatorStore,
|
||||||
// dap_command::{
|
|
||||||
// ContinueCommand, DapCommand, DisconnectCommand, NextCommand, PauseCommand, RestartCommand,
|
|
||||||
// RestartStackFrameCommand, StepBackCommand, StepCommand, StepInCommand, StepOutCommand,
|
|
||||||
// TerminateCommand, TerminateThreadsCommand, VariablesCommand,
|
|
||||||
// },
|
|
||||||
session::{self, Session},
|
session::{self, Session},
|
||||||
};
|
};
|
||||||
use crate::{debugger, worktree_store::WorktreeStore, ProjectEnvironment};
|
use crate::{debugger, worktree_store::WorktreeStore, ProjectEnvironment};
|
||||||
|
@ -87,6 +82,7 @@ pub struct LocalDapStore {
|
||||||
language_registry: Arc<LanguageRegistry>,
|
language_registry: Arc<LanguageRegistry>,
|
||||||
debug_adapters: Arc<DapRegistry>,
|
debug_adapters: Arc<DapRegistry>,
|
||||||
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
toolchain_store: Arc<dyn LanguageToolchainStore>,
|
||||||
|
locator_store: Arc<LocatorStore>,
|
||||||
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
start_debugging_tx: futures::channel::mpsc::UnboundedSender<(SessionId, Message)>,
|
||||||
_start_debugging_task: Task<()>,
|
_start_debugging_task: Task<()>,
|
||||||
}
|
}
|
||||||
|
@ -179,6 +175,7 @@ impl DapStore {
|
||||||
debug_adapters,
|
debug_adapters,
|
||||||
start_debugging_tx,
|
start_debugging_tx,
|
||||||
_start_debugging_task,
|
_start_debugging_task,
|
||||||
|
locator_store: Arc::from(LocatorStore::new()),
|
||||||
next_session_id: Default::default(),
|
next_session_id: Default::default(),
|
||||||
}),
|
}),
|
||||||
downstream_client: None,
|
downstream_client: None,
|
||||||
|
@ -324,7 +321,7 @@ impl DapStore {
|
||||||
|
|
||||||
pub fn new_session(
|
pub fn new_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
config: DebugAdapterConfig,
|
mut config: DebugAdapterConfig,
|
||||||
worktree: &Entity<Worktree>,
|
worktree: &Entity<Worktree>,
|
||||||
parent_session: Option<Entity<Session>>,
|
parent_session: Option<Entity<Session>>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
|
@ -354,22 +351,39 @@ impl DapStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (initialized_tx, initialized_rx) = oneshot::channel();
|
let (initialized_tx, initialized_rx) = oneshot::channel();
|
||||||
|
let locator_store = local_store.locator_store.clone();
|
||||||
|
let debug_adapters = local_store.debug_adapters.clone();
|
||||||
|
|
||||||
let start_client_task = Session::local(
|
let start_debugging_tx = local_store.start_debugging_tx.clone();
|
||||||
self.breakpoint_store.clone(),
|
|
||||||
session_id,
|
let task = cx.spawn(async move |this, cx| {
|
||||||
parent_session,
|
if config.locator.is_some() {
|
||||||
delegate,
|
locator_store.resolve_debug_config(&mut config).await?;
|
||||||
config,
|
}
|
||||||
local_store.start_debugging_tx.clone(),
|
|
||||||
initialized_tx,
|
let start_client_task = this.update(cx, |this, cx| {
|
||||||
local_store.debug_adapters.clone(),
|
Session::local(
|
||||||
cx,
|
this.breakpoint_store.clone(),
|
||||||
);
|
session_id,
|
||||||
|
parent_session,
|
||||||
|
delegate,
|
||||||
|
config,
|
||||||
|
start_debugging_tx.clone(),
|
||||||
|
initialized_tx,
|
||||||
|
debug_adapters,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
this.update(cx, |_, cx| {
|
||||||
|
create_new_session(session_id, initialized_rx, start_client_task, cx)
|
||||||
|
})?
|
||||||
|
.await
|
||||||
|
});
|
||||||
|
|
||||||
let task = create_new_session(session_id, initialized_rx, start_client_task, cx);
|
|
||||||
(session_id, task)
|
(session_id, task)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
pub fn new_fake_session(
|
pub fn new_fake_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -456,7 +470,10 @@ impl DapStore {
|
||||||
request: DebugRequestDisposition::ReverseRequest(args),
|
request: DebugRequestDisposition::ReverseRequest(args),
|
||||||
initialize_args: config.initialize_args.clone(),
|
initialize_args: config.initialize_args.clone(),
|
||||||
tcp_connection: config.tcp_connection.clone(),
|
tcp_connection: config.tcp_connection.clone(),
|
||||||
|
locator: None,
|
||||||
|
args: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test-support"))]
|
#[cfg(any(test, feature = "test-support"))]
|
||||||
let new_session_task = {
|
let new_session_task = {
|
||||||
let caps = parent_session.read(cx).capabilities.clone();
|
let caps = parent_session.read(cx).capabilities.clone();
|
||||||
|
|
39
crates/project/src/debugger/locator_store.rs
Normal file
39
crates/project/src/debugger/locator_store.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use cargo::CargoLocator;
|
||||||
|
use collections::HashMap;
|
||||||
|
use dap::DebugAdapterConfig;
|
||||||
|
use gpui::SharedString;
|
||||||
|
use locators::DapLocator;
|
||||||
|
|
||||||
|
mod cargo;
|
||||||
|
mod locators;
|
||||||
|
|
||||||
|
pub(super) struct LocatorStore {
|
||||||
|
locators: HashMap<SharedString, Box<dyn DapLocator>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocatorStore {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
let locators = HashMap::from_iter([(
|
||||||
|
SharedString::new("cargo"),
|
||||||
|
Box::new(CargoLocator {}) as Box<dyn DapLocator>,
|
||||||
|
)]);
|
||||||
|
Self { locators }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn resolve_debug_config(
|
||||||
|
&self,
|
||||||
|
debug_config: &mut DebugAdapterConfig,
|
||||||
|
) -> Result<()> {
|
||||||
|
let Some(ref locator_name) = &debug_config.locator else {
|
||||||
|
log::debug!("Attempted to resolve debug config without a locator field");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(locator) = self.locators.get(locator_name as &str) {
|
||||||
|
locator.run_locator(debug_config).await
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("Couldn't find locator {}", locator_name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
crates/project/src/debugger/locator_store/cargo.rs
Normal file
65
crates/project/src/debugger/locator_store/cargo.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use super::DapLocator;
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use dap::DebugAdapterConfig;
|
||||||
|
use serde_json::Value;
|
||||||
|
use smol::{
|
||||||
|
io::AsyncReadExt,
|
||||||
|
process::{Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) struct CargoLocator {}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl DapLocator for CargoLocator {
|
||||||
|
async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()> {
|
||||||
|
let Some(launch_config) = (match &mut debug_config.request {
|
||||||
|
task::DebugRequestDisposition::UserConfigured(task::DebugRequestType::Launch(
|
||||||
|
launch_config,
|
||||||
|
)) => Some(launch_config),
|
||||||
|
_ => None,
|
||||||
|
}) else {
|
||||||
|
return Err(anyhow!("Couldn't get launch config in locator"));
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(cwd) = launch_config.cwd.clone() else {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Couldn't get cwd from debug config which is needed for locators"
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut child = Command::new("cargo")
|
||||||
|
.args(&debug_config.args)
|
||||||
|
.arg("--message-format=json")
|
||||||
|
.current_dir(cwd)
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.spawn()?;
|
||||||
|
|
||||||
|
let mut output = String::new();
|
||||||
|
if let Some(mut stdout) = child.stdout.take() {
|
||||||
|
stdout.read_to_string(&mut output).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let status = child.status().await?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err(anyhow::anyhow!("Cargo command failed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(executable) = output
|
||||||
|
.lines()
|
||||||
|
.filter(|line| !line.trim().is_empty())
|
||||||
|
.filter_map(|line| serde_json::from_str(line).ok())
|
||||||
|
.find_map(|json: Value| {
|
||||||
|
json.get("executable")
|
||||||
|
.and_then(Value::as_str)
|
||||||
|
.map(String::from)
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
return Err(anyhow!("Couldn't get executable in cargo locator"));
|
||||||
|
};
|
||||||
|
|
||||||
|
launch_config.program = executable;
|
||||||
|
debug_config.args.clear();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
8
crates/project/src/debugger/locator_store/locators.rs
Normal file
8
crates/project/src/debugger/locator_store/locators.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use dap::DebugAdapterConfig;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub(super) trait DapLocator {
|
||||||
|
async fn run_locator(&self, debug_config: &mut DebugAdapterConfig) -> Result<()>;
|
||||||
|
}
|
|
@ -165,6 +165,7 @@ pub struct Project {
|
||||||
languages: Arc<LanguageRegistry>,
|
languages: Arc<LanguageRegistry>,
|
||||||
debug_adapters: Arc<DapRegistry>,
|
debug_adapters: Arc<DapRegistry>,
|
||||||
dap_store: Entity<DapStore>,
|
dap_store: Entity<DapStore>,
|
||||||
|
|
||||||
breakpoint_store: Entity<BreakpointStore>,
|
breakpoint_store: Entity<BreakpointStore>,
|
||||||
client: Arc<client::Client>,
|
client: Arc<client::Client>,
|
||||||
join_project_response_message_id: u32,
|
join_project_response_message_id: u32,
|
||||||
|
@ -952,6 +953,7 @@ impl Project {
|
||||||
ssh_client: None,
|
ssh_client: None,
|
||||||
breakpoint_store,
|
breakpoint_store,
|
||||||
dap_store,
|
dap_store,
|
||||||
|
|
||||||
buffers_needing_diff: Default::default(),
|
buffers_needing_diff: Default::default(),
|
||||||
git_diff_debouncer: DebouncedDelay::new(),
|
git_diff_debouncer: DebouncedDelay::new(),
|
||||||
terminals: Terminals {
|
terminals: Terminals {
|
||||||
|
@ -1450,6 +1452,12 @@ impl Project {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn queue_debug_session(&mut self, config: DebugAdapterConfig, cx: &mut Context<Self>) {
|
||||||
|
if config.locator.is_none() {
|
||||||
|
self.start_debug_session(config, cx).detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start_debug_session(
|
pub fn start_debug_session(
|
||||||
&mut self,
|
&mut self,
|
||||||
config: DebugAdapterConfig,
|
config: DebugAdapterConfig,
|
||||||
|
@ -1490,6 +1498,8 @@ impl Project {
|
||||||
request: DebugRequestDisposition::UserConfigured(request),
|
request: DebugRequestDisposition::UserConfigured(request),
|
||||||
initialize_args: None,
|
initialize_args: None,
|
||||||
tcp_connection: None,
|
tcp_connection: None,
|
||||||
|
locator: None,
|
||||||
|
args: Default::default(),
|
||||||
};
|
};
|
||||||
let caps = caps.unwrap_or(Capabilities {
|
let caps = caps.unwrap_or(Capabilities {
|
||||||
supports_step_back: Some(false),
|
supports_step_back: Some(false),
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::net::Ipv4Addr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::{TaskTemplate, TaskTemplates, TaskType};
|
use crate::{task_template::DebugArgs, TaskTemplate, TaskTemplates, TaskType};
|
||||||
|
|
||||||
impl Default for DebugConnectionType {
|
impl Default for DebugConnectionType {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -102,6 +102,10 @@ pub struct DebugAdapterConfig {
|
||||||
/// spawning a new process. This is useful for connecting to a debug adapter
|
/// spawning a new process. This is useful for connecting to a debug adapter
|
||||||
/// that is already running or is started by another process.
|
/// that is already running or is started by another process.
|
||||||
pub tcp_connection: Option<TCPHost>,
|
pub tcp_connection: Option<TCPHost>,
|
||||||
|
/// What Locator to use to configure the debug task
|
||||||
|
pub locator: Option<String>,
|
||||||
|
/// Args to pass to a debug adapter (only used in locator right now)
|
||||||
|
pub args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DebugTaskDefinition> for DebugAdapterConfig {
|
impl From<DebugTaskDefinition> for DebugAdapterConfig {
|
||||||
|
@ -112,6 +116,8 @@ impl From<DebugTaskDefinition> for DebugAdapterConfig {
|
||||||
request: DebugRequestDisposition::UserConfigured(def.request),
|
request: DebugRequestDisposition::UserConfigured(def.request),
|
||||||
initialize_args: def.initialize_args,
|
initialize_args: def.initialize_args,
|
||||||
tcp_connection: def.tcp_connection,
|
tcp_connection: def.tcp_connection,
|
||||||
|
locator: def.locator,
|
||||||
|
args: def.args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,6 +136,8 @@ impl TryFrom<DebugAdapterConfig> for DebugTaskDefinition {
|
||||||
request,
|
request,
|
||||||
initialize_args: def.initialize_args,
|
initialize_args: def.initialize_args,
|
||||||
tcp_connection: def.tcp_connection,
|
tcp_connection: def.tcp_connection,
|
||||||
|
locator: def.locator,
|
||||||
|
args: def.args,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,18 +145,30 @@ impl TryFrom<DebugAdapterConfig> for DebugTaskDefinition {
|
||||||
impl DebugTaskDefinition {
|
impl DebugTaskDefinition {
|
||||||
/// Translate from debug definition to a task template
|
/// Translate from debug definition to a task template
|
||||||
pub fn to_zed_format(self) -> anyhow::Result<TaskTemplate> {
|
pub fn to_zed_format(self) -> anyhow::Result<TaskTemplate> {
|
||||||
let command = "".to_string();
|
let (command, cwd, request) = match self.request {
|
||||||
|
DebugRequestType::Launch(launch_config) => (
|
||||||
let cwd = if let DebugRequestType::Launch(ref launch) = self.request {
|
launch_config.program,
|
||||||
launch
|
launch_config
|
||||||
.cwd
|
.cwd
|
||||||
.as_ref()
|
.map(|cwd| cwd.to_string_lossy().to_string()),
|
||||||
.map(|path| path.to_string_lossy().into_owned())
|
crate::task_template::DebugArgsRequest::Launch,
|
||||||
} else {
|
),
|
||||||
None
|
DebugRequestType::Attach(attach_config) => (
|
||||||
|
"".to_owned(),
|
||||||
|
None,
|
||||||
|
crate::task_template::DebugArgsRequest::Attach(attach_config),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let task_type = TaskType::Debug(DebugArgs {
|
||||||
|
adapter: self.adapter,
|
||||||
|
request,
|
||||||
|
initialize_args: self.initialize_args,
|
||||||
|
locator: self.locator,
|
||||||
|
tcp_connection: self.tcp_connection,
|
||||||
|
});
|
||||||
|
|
||||||
let label = self.label.clone();
|
let label = self.label.clone();
|
||||||
let task_type = TaskType::Debug(self);
|
|
||||||
|
|
||||||
Ok(TaskTemplate {
|
Ok(TaskTemplate {
|
||||||
label,
|
label,
|
||||||
|
@ -189,6 +209,12 @@ pub struct DebugTaskDefinition {
|
||||||
/// spawning a new process. This is useful for connecting to a debug adapter
|
/// spawning a new process. This is useful for connecting to a debug adapter
|
||||||
/// that is already running or is started by another process.
|
/// that is already running or is started by another process.
|
||||||
pub tcp_connection: Option<TCPHost>,
|
pub tcp_connection: Option<TCPHost>,
|
||||||
|
/// Locator to use
|
||||||
|
/// -- cargo
|
||||||
|
pub locator: Option<String>,
|
||||||
|
/// Args to pass to a debug adapter (only used in locator right now)
|
||||||
|
#[serde(skip)]
|
||||||
|
pub args: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A group of Debug Tasks defined in a JSON file.
|
/// A group of Debug Tasks defined in a JSON file.
|
||||||
|
|
|
@ -19,7 +19,8 @@ pub use debug_format::{
|
||||||
DebugRequestType, DebugTaskDefinition, DebugTaskFile, LaunchConfig, TCPHost,
|
DebugRequestType, DebugTaskDefinition, DebugTaskFile, LaunchConfig, TCPHost,
|
||||||
};
|
};
|
||||||
pub use task_template::{
|
pub use task_template::{
|
||||||
HideStrategy, RevealStrategy, TaskModal, TaskTemplate, TaskTemplates, TaskType,
|
DebugArgs, DebugArgsRequest, HideStrategy, RevealStrategy, TaskModal, TaskTemplate,
|
||||||
|
TaskTemplates, TaskType,
|
||||||
};
|
};
|
||||||
pub use vscode_format::VsCodeTaskFile;
|
pub use vscode_format::VsCodeTaskFile;
|
||||||
pub use zed_actions::RevealTarget;
|
pub use zed_actions::RevealTarget;
|
||||||
|
@ -61,8 +62,6 @@ pub struct SpawnInTerminal {
|
||||||
pub hide: HideStrategy,
|
pub hide: HideStrategy,
|
||||||
/// Which shell to use when spawning the task.
|
/// Which shell to use when spawning the task.
|
||||||
pub shell: Shell,
|
pub shell: Shell,
|
||||||
/// Tells debug tasks which program to debug
|
|
||||||
pub program: Option<String>,
|
|
||||||
/// Whether to show the task summary line in the task output (sucess/failure).
|
/// Whether to show the task summary line in the task output (sucess/failure).
|
||||||
pub show_summary: bool,
|
pub show_summary: bool,
|
||||||
/// Whether to show the command line in the task output.
|
/// Whether to show the command line in the task output.
|
||||||
|
@ -104,24 +103,50 @@ impl ResolvedTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the configuration for the debug adapter that should be used for this task.
|
/// Get the configuration for the debug adapter that should be used for this task.
|
||||||
pub fn resolved_debug_adapter_config(&self) -> Option<DebugTaskDefinition> {
|
pub fn resolved_debug_adapter_config(&self) -> Option<DebugAdapterConfig> {
|
||||||
match self.original_task.task_type.clone() {
|
match self.original_task.task_type.clone() {
|
||||||
TaskType::Script => None,
|
TaskType::Debug(debug_args) if self.resolved.is_some() => {
|
||||||
TaskType::Debug(mut adapter_config) => {
|
let resolved = self
|
||||||
if let Some(resolved) = &self.resolved {
|
.resolved
|
||||||
adapter_config.label = resolved.label.clone();
|
.as_ref()
|
||||||
if let DebugRequestType::Launch(ref mut launch) = adapter_config.request {
|
.expect("We just checked if this was some");
|
||||||
if let Some(program) = resolved.program.clone() {
|
|
||||||
launch.program = program;
|
|
||||||
}
|
|
||||||
if let Some(cwd) = resolved.cwd.clone() {
|
|
||||||
launch.cwd = Some(cwd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(adapter_config)
|
let args = resolved
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|arg| {
|
||||||
|
if arg.starts_with("$") {
|
||||||
|
arg.strip_prefix("$")
|
||||||
|
.and_then(|arg| resolved.env.get(arg).map(ToOwned::to_owned))
|
||||||
|
.unwrap_or_else(|| arg)
|
||||||
|
} else {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Some(DebugAdapterConfig {
|
||||||
|
label: resolved.label.clone(),
|
||||||
|
adapter: debug_args.adapter.clone(),
|
||||||
|
request: DebugRequestDisposition::UserConfigured(match debug_args.request {
|
||||||
|
crate::task_template::DebugArgsRequest::Launch => {
|
||||||
|
DebugRequestType::Launch(LaunchConfig {
|
||||||
|
program: resolved.command.clone(),
|
||||||
|
cwd: resolved.cwd.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
crate::task_template::DebugArgsRequest::Attach(attach_config) => {
|
||||||
|
DebugRequestType::Attach(attach_config)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
initialize_args: debug_args.initialize_args,
|
||||||
|
tcp_connection: debug_args.tcp_connection,
|
||||||
|
args,
|
||||||
|
locator: debug_args.locator.clone(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ use sha2::{Digest, Sha256};
|
||||||
use util::{truncate_and_remove_front, ResultExt};
|
use util::{truncate_and_remove_front, ResultExt};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DebugRequestType, DebugTaskDefinition, ResolvedTask, RevealTarget, Shell, SpawnInTerminal,
|
AttachConfig, ResolvedTask, RevealTarget, Shell, SpawnInTerminal, TCPHost, TaskContext, TaskId,
|
||||||
TaskContext, TaskId, VariableName, ZED_VARIABLE_NAME_PREFIX,
|
VariableName, ZED_VARIABLE_NAME_PREFIX,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A template definition of a Zed task to run.
|
/// A template definition of a Zed task to run.
|
||||||
|
@ -75,62 +75,39 @@ pub struct TaskTemplate {
|
||||||
pub show_command: bool,
|
pub show_command: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Eq, PartialEq, Clone, Debug)]
|
||||||
|
/// Use to represent debug request type
|
||||||
|
pub enum DebugArgsRequest {
|
||||||
|
/// launch (program, cwd) are stored in TaskTemplate as (command, cwd)
|
||||||
|
Launch,
|
||||||
|
/// Attach
|
||||||
|
Attach(AttachConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Eq, PartialEq, Clone, Debug)]
|
||||||
|
/// This represents the arguments for the debug task.
|
||||||
|
pub struct DebugArgs {
|
||||||
|
/// The launch type
|
||||||
|
pub request: DebugArgsRequest,
|
||||||
|
/// Adapter choice
|
||||||
|
pub adapter: String,
|
||||||
|
/// TCP connection to make with debug adapter
|
||||||
|
pub tcp_connection: Option<TCPHost>,
|
||||||
|
/// Args to send to debug adapter
|
||||||
|
pub initialize_args: Option<serde_json::value::Value>,
|
||||||
|
/// the locator to use
|
||||||
|
pub locator: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the type of task that is being ran
|
/// Represents the type of task that is being ran
|
||||||
#[derive(Default, Deserialize, Serialize, Eq, PartialEq, JsonSchema, Clone, Debug)]
|
#[derive(Default, Eq, PartialEq, Clone, Debug)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
|
||||||
#[allow(clippy::large_enum_variant)]
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum TaskType {
|
pub enum TaskType {
|
||||||
/// Act like a typically task that runs commands
|
/// Act like a typically task that runs commands
|
||||||
#[default]
|
#[default]
|
||||||
Script,
|
Script,
|
||||||
/// This task starts the debugger for a language
|
/// This task starts the debugger for a language
|
||||||
Debug(DebugTaskDefinition),
|
Debug(DebugArgs),
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod deserialization_tests {
|
|
||||||
use crate::LaunchConfig;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_task_type_script() {
|
|
||||||
let json = json!({"type": "script"});
|
|
||||||
|
|
||||||
let task_type: TaskType =
|
|
||||||
serde_json::from_value(json).expect("Failed to deserialize TaskType::Script");
|
|
||||||
assert_eq!(task_type, TaskType::Script);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn deserialize_task_type_debug() {
|
|
||||||
let adapter_config = DebugTaskDefinition {
|
|
||||||
label: "test config".into(),
|
|
||||||
adapter: "Debugpy".into(),
|
|
||||||
request: crate::DebugRequestType::Launch(LaunchConfig {
|
|
||||||
program: "main".to_string(),
|
|
||||||
cwd: None,
|
|
||||||
}),
|
|
||||||
initialize_args: None,
|
|
||||||
tcp_connection: None,
|
|
||||||
};
|
|
||||||
let json = json!({
|
|
||||||
"label": "test config",
|
|
||||||
"type": "debug",
|
|
||||||
"adapter": "Debugpy",
|
|
||||||
"program": "main",
|
|
||||||
"supports_attach": false,
|
|
||||||
});
|
|
||||||
|
|
||||||
let task_type: TaskType =
|
|
||||||
serde_json::from_value(json).expect("Failed to deserialize TaskType::Debug");
|
|
||||||
if let TaskType::Debug(config) = task_type {
|
|
||||||
assert_eq!(config, adapter_config);
|
|
||||||
} else {
|
|
||||||
panic!("Expected TaskType::Debug");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -270,22 +247,6 @@ impl TaskTemplate {
|
||||||
&mut substituted_variables,
|
&mut substituted_variables,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let program = match &self.task_type {
|
|
||||||
TaskType::Script => None,
|
|
||||||
TaskType::Debug(adapter_config) => {
|
|
||||||
if let DebugRequestType::Launch(ref launch) = &adapter_config.request {
|
|
||||||
Some(substitute_all_template_variables_in_str(
|
|
||||||
&launch.program,
|
|
||||||
&task_variables,
|
|
||||||
&variable_names,
|
|
||||||
&mut substituted_variables,
|
|
||||||
)?)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let task_hash = to_hex_hash(self)
|
let task_hash = to_hex_hash(self)
|
||||||
.context("hashing task template")
|
.context("hashing task template")
|
||||||
.log_err()?;
|
.log_err()?;
|
||||||
|
@ -341,7 +302,6 @@ impl TaskTemplate {
|
||||||
reveal_target: self.reveal_target,
|
reveal_target: self.reveal_target,
|
||||||
hide: self.hide,
|
hide: self.hide,
|
||||||
shell: self.shell.clone(),
|
shell: self.shell.clone(),
|
||||||
program,
|
|
||||||
show_summary: self.show_summary,
|
show_summary: self.show_summary,
|
||||||
show_command: self.show_command,
|
show_command: self.show_command,
|
||||||
show_rerun: true,
|
show_rerun: true,
|
||||||
|
|
|
@ -10,7 +10,8 @@ use gpui::{
|
||||||
use picker::{highlighted_match_with_paths::HighlightedMatch, Picker, PickerDelegate};
|
use picker::{highlighted_match_with_paths::HighlightedMatch, Picker, PickerDelegate};
|
||||||
use project::{task_store::TaskStore, TaskSourceKind};
|
use project::{task_store::TaskStore, TaskSourceKind};
|
||||||
use task::{
|
use task::{
|
||||||
DebugRequestType, ResolvedTask, RevealTarget, TaskContext, TaskModal, TaskTemplate, TaskType,
|
DebugRequestType, DebugTaskDefinition, ResolvedTask, RevealTarget, TaskContext, TaskModal,
|
||||||
|
TaskTemplate, TaskType,
|
||||||
};
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
div, h_flex, v_flex, ActiveTheme, Button, ButtonCommon, ButtonSize, Clickable, Color,
|
div, h_flex, v_flex, ActiveTheme, Button, ButtonCommon, ButtonSize, Clickable, Color,
|
||||||
|
@ -320,15 +321,11 @@ impl PickerDelegate for TasksModalDelegate {
|
||||||
self.workspace
|
self.workspace
|
||||||
.update(cx, |workspace, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
match task.task_type() {
|
match task.task_type() {
|
||||||
TaskType::Script => schedule_resolved_task(
|
TaskType::Debug(config) if config.locator.is_none() => {
|
||||||
workspace,
|
let Some(config): Option<DebugTaskDefinition> = task
|
||||||
task_source_kind,
|
.resolved_debug_adapter_config()
|
||||||
task,
|
.and_then(|config| config.try_into().ok())
|
||||||
omit_history_entry,
|
else {
|
||||||
cx,
|
|
||||||
),
|
|
||||||
TaskType::Debug(_) => {
|
|
||||||
let Some(config) = task.resolved_debug_adapter_config() else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let project = workspace.project().clone();
|
let project = workspace.project().clone();
|
||||||
|
@ -355,6 +352,13 @@ impl PickerDelegate for TasksModalDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => schedule_resolved_task(
|
||||||
|
workspace,
|
||||||
|
task_source_kind,
|
||||||
|
task,
|
||||||
|
omit_history_entry,
|
||||||
|
cx,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -517,16 +521,30 @@ impl PickerDelegate for TasksModalDelegate {
|
||||||
omit_history_entry,
|
omit_history_entry,
|
||||||
cx,
|
cx,
|
||||||
),
|
),
|
||||||
// TODO: Should create a schedule_resolved_debug_task function
|
// todo(debugger): Should create a schedule_resolved_debug_task function
|
||||||
// This would allow users to access to debug history and other issues
|
// This would allow users to access to debug history and other issues
|
||||||
TaskType::Debug(_) => workspace.project().update(cx, |project, cx| {
|
TaskType::Debug(debug_args) => {
|
||||||
project
|
let Some(debug_config) = task.resolved_debug_adapter_config() else {
|
||||||
.start_debug_session(
|
// todo(debugger) log an error, this should never happen
|
||||||
task.resolved_debug_adapter_config().unwrap().into(),
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if debug_args.locator.is_some() {
|
||||||
|
schedule_resolved_task(
|
||||||
|
workspace,
|
||||||
|
task_source_kind,
|
||||||
|
task,
|
||||||
|
omit_history_entry,
|
||||||
cx,
|
cx,
|
||||||
)
|
);
|
||||||
.detach_and_log_err(cx);
|
} else {
|
||||||
}),
|
workspace.project().update(cx, |project, cx| {
|
||||||
|
project
|
||||||
|
.start_debug_session(debug_config, cx)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.ok();
|
.ok();
|
||||||
|
|
|
@ -110,6 +110,7 @@ pub enum Event {
|
||||||
SelectionsChanged,
|
SelectionsChanged,
|
||||||
NewNavigationTarget(Option<MaybeNavigationTarget>),
|
NewNavigationTarget(Option<MaybeNavigationTarget>),
|
||||||
Open(MaybeNavigationTarget),
|
Open(MaybeNavigationTarget),
|
||||||
|
TaskLocatorReady { task_id: TaskId, success: bool },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -1899,6 +1900,11 @@ impl Terminal {
|
||||||
unsafe { append_text_to_term(&mut self.term.lock(), &lines_to_show) };
|
unsafe { append_text_to_term(&mut self.term.lock(), &lines_to_show) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cx.emit(Event::TaskLocatorReady {
|
||||||
|
task_id: task.id.clone(),
|
||||||
|
success: finished_successfully,
|
||||||
|
});
|
||||||
|
|
||||||
match task.hide {
|
match task.hide {
|
||||||
HideStrategy::Never => {}
|
HideStrategy::Never => {}
|
||||||
HideStrategy::Always => {
|
HideStrategy::Always => {
|
||||||
|
|
|
@ -982,6 +982,15 @@ fn subscribe_for_terminal_events(
|
||||||
window.invalidate_character_coordinates();
|
window.invalidate_character_coordinates();
|
||||||
cx.emit(SearchEvent::ActiveMatchChanged)
|
cx.emit(SearchEvent::ActiveMatchChanged)
|
||||||
}
|
}
|
||||||
|
Event::TaskLocatorReady { task_id, success } => {
|
||||||
|
if *success {
|
||||||
|
workspace
|
||||||
|
.update(cx, |workspace, cx| {
|
||||||
|
workspace.debug_task_ready(task_id, cx);
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
vec![terminal_subscription, terminal_events_subscription]
|
vec![terminal_subscription, terminal_events_subscription]
|
||||||
|
|
|
@ -1440,7 +1440,6 @@ impl ShellExec {
|
||||||
reveal_target: RevealTarget::Dock,
|
reveal_target: RevealTarget::Dock,
|
||||||
hide: HideStrategy::Never,
|
hide: HideStrategy::Never,
|
||||||
shell,
|
shell,
|
||||||
program: None,
|
|
||||||
show_summary: false,
|
show_summary: false,
|
||||||
show_command: false,
|
show_command: false,
|
||||||
show_rerun: false,
|
show_rerun: false,
|
||||||
|
|
|
@ -46,7 +46,15 @@ pub fn schedule_resolved_task(
|
||||||
omit_history: bool,
|
omit_history: bool,
|
||||||
cx: &mut Context<Workspace>,
|
cx: &mut Context<Workspace>,
|
||||||
) {
|
) {
|
||||||
|
let debug_config = resolved_task.resolved_debug_adapter_config();
|
||||||
|
|
||||||
if let Some(spawn_in_terminal) = resolved_task.resolved.take() {
|
if let Some(spawn_in_terminal) = resolved_task.resolved.take() {
|
||||||
|
if let Some(debug_config) = debug_config {
|
||||||
|
workspace
|
||||||
|
.debug_task_queue
|
||||||
|
.insert(resolved_task.id.clone(), debug_config);
|
||||||
|
}
|
||||||
|
|
||||||
if !omit_history {
|
if !omit_history {
|
||||||
resolved_task.resolved = Some(spawn_in_terminal.clone());
|
resolved_task.resolved = Some(spawn_in_terminal.clone());
|
||||||
workspace.project().update(cx, |project, cx| {
|
workspace.project().update(cx, |project, cx| {
|
||||||
|
|
|
@ -94,7 +94,7 @@ use std::{
|
||||||
sync::{atomic::AtomicUsize, Arc, LazyLock, Weak},
|
sync::{atomic::AtomicUsize, Arc, LazyLock, Weak},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use task::SpawnInTerminal;
|
use task::{DebugAdapterConfig, SpawnInTerminal, TaskId};
|
||||||
use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
|
use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
|
||||||
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
||||||
pub use ui;
|
pub use ui;
|
||||||
|
@ -874,6 +874,7 @@ pub struct Workspace {
|
||||||
serialized_ssh_project: Option<SerializedSshProject>,
|
serialized_ssh_project: Option<SerializedSshProject>,
|
||||||
_items_serializer: Task<Result<()>>,
|
_items_serializer: Task<Result<()>>,
|
||||||
session_id: Option<String>,
|
session_id: Option<String>,
|
||||||
|
debug_task_queue: HashMap<task::TaskId, DebugAdapterConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventEmitter<Event> for Workspace {}
|
impl EventEmitter<Event> for Workspace {}
|
||||||
|
@ -1185,6 +1186,7 @@ impl Workspace {
|
||||||
_items_serializer,
|
_items_serializer,
|
||||||
session_id: Some(session_id),
|
session_id: Some(session_id),
|
||||||
serialized_ssh_project: None,
|
serialized_ssh_project: None,
|
||||||
|
debug_task_queue: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5191,6 +5193,16 @@ impl Workspace {
|
||||||
.update(cx, |_, window, _| window.activate_window())
|
.update(cx, |_, window, _| window.activate_window())
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn debug_task_ready(&mut self, task_id: &TaskId, cx: &mut App) {
|
||||||
|
if let Some(debug_config) = self.debug_task_queue.remove(task_id) {
|
||||||
|
self.project.update(cx, |project, cx| {
|
||||||
|
project
|
||||||
|
.start_debug_session(debug_config, cx)
|
||||||
|
.detach_and_log_err(cx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn leader_border_for_pane(
|
fn leader_border_for_pane(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue