debugger: Run build in terminal (#29645)
Currently contains the pre-work of making sessions creatable without a definition, but still need to change the spawn in terminal to use the running session Release Notes: - N/A --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
parent
c12e6376b8
commit
ff215b4f11
12 changed files with 695 additions and 622 deletions
|
@ -6,10 +6,9 @@ use crate::{
|
|||
persistence,
|
||||
};
|
||||
use crate::{new_session_modal::NewSessionModal, session::DebugSession};
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use collections::{HashMap, HashSet};
|
||||
use anyhow::Result;
|
||||
use command_palette_hooks::CommandPaletteFilter;
|
||||
use dap::DebugRequest;
|
||||
use dap::adapters::DebugAdapterName;
|
||||
use dap::{
|
||||
ContinuedEvent, LoadedSourceEvent, ModuleEvent, OutputEvent, StoppedEvent, ThreadEvent,
|
||||
client::SessionId, debugger_settings::DebuggerSettings,
|
||||
|
@ -27,7 +26,6 @@ use project::{Project, debugger::session::ThreadStatus};
|
|||
use rpc::proto::{self};
|
||||
use settings::Settings;
|
||||
use std::any::TypeId;
|
||||
use std::path::PathBuf;
|
||||
use task::{DebugScenario, TaskContext};
|
||||
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
|
||||
use workspace::SplitDirection;
|
||||
|
@ -200,40 +198,6 @@ impl DebugPanel {
|
|||
})
|
||||
}
|
||||
|
||||
fn start_from_definition(
|
||||
&mut self,
|
||||
definition: DebugTaskDefinition,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<()>> {
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let dap_store = this.update(cx, |this, cx| this.project.read(cx).dap_store())?;
|
||||
let (session, task) = dap_store.update(cx, |dap_store, cx| {
|
||||
let session = dap_store.new_session(definition, None, cx);
|
||||
|
||||
(session.clone(), dap_store.boot_session(session, cx))
|
||||
})?;
|
||||
Self::register_session(this.clone(), session.clone(), cx).await?;
|
||||
|
||||
if let Err(e) = task.await {
|
||||
this.update(cx, |this, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.show_error(&e, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
|
||||
session
|
||||
.update(cx, |session, cx| session.shutdown(cx))?
|
||||
.await;
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start_session(
|
||||
&mut self,
|
||||
scenario: DebugScenario,
|
||||
|
@ -242,16 +206,56 @@ impl DebugPanel {
|
|||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let definition = this
|
||||
.update_in(cx, |this, window, cx| {
|
||||
this.resolve_scenario(scenario, task_context, active_buffer, window, cx)
|
||||
})?
|
||||
.await?;
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.start_from_definition(definition, window, cx)
|
||||
})?
|
||||
.await
|
||||
let dap_store = self.project.read(cx).dap_store();
|
||||
let workspace = self.workspace.clone();
|
||||
let session = dap_store.update(cx, |dap_store, cx| {
|
||||
dap_store.new_session(
|
||||
scenario.label.clone(),
|
||||
DebugAdapterName(scenario.adapter.clone()),
|
||||
None,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
let task = cx.spawn_in(window, {
|
||||
let session = session.clone();
|
||||
async move |this, cx| {
|
||||
let debug_session =
|
||||
Self::register_session(this.clone(), session.clone(), cx).await?;
|
||||
let definition = debug_session
|
||||
.update_in(cx, |debug_session, window, cx| {
|
||||
debug_session.running_state().update(cx, |running, cx| {
|
||||
running.resolve_scenario(
|
||||
scenario,
|
||||
task_context,
|
||||
active_buffer,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
})?
|
||||
.await?;
|
||||
|
||||
dap_store
|
||||
.update(cx, |dap_store, cx| {
|
||||
dap_store.boot_session(session.clone(), definition, cx)
|
||||
})?
|
||||
.await
|
||||
}
|
||||
});
|
||||
|
||||
cx.spawn(async move |_, cx| {
|
||||
if let Err(error) = task.await {
|
||||
log::error!("{:?}", error);
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.show_error(&error, cx);
|
||||
})
|
||||
.ok();
|
||||
session
|
||||
.update(cx, |session, cx| session.shutdown(cx))?
|
||||
.await;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
@ -260,33 +264,15 @@ impl DebugPanel {
|
|||
this: WeakEntity<Self>,
|
||||
session: Entity<Session>,
|
||||
cx: &mut AsyncWindowContext,
|
||||
) -> Result<()> {
|
||||
let adapter_name = session.update(cx, |session, _| session.adapter_name())?;
|
||||
) -> Result<Entity<DebugSession>> {
|
||||
let adapter_name = session.update(cx, |session, _| session.adapter())?;
|
||||
this.update_in(cx, |_, window, cx| {
|
||||
cx.subscribe_in(
|
||||
&session,
|
||||
window,
|
||||
move |this, session, event: &SessionStateEvent, window, cx| match event {
|
||||
SessionStateEvent::Restart => {
|
||||
let mut curr_session = session.clone();
|
||||
while let Some(parent_session) = curr_session
|
||||
.read_with(cx, |session, _| session.parent_session().cloned())
|
||||
{
|
||||
curr_session = parent_session;
|
||||
}
|
||||
|
||||
let definition = curr_session.update(cx, |session, _| session.definition());
|
||||
let task = curr_session.update(cx, |session, cx| session.shutdown(cx));
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
task.await;
|
||||
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.start_from_definition(definition, window, cx)
|
||||
})?
|
||||
.await
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
this.handle_restart_request(session.clone(), window, cx);
|
||||
}
|
||||
SessionStateEvent::SpawnChildSession { request } => {
|
||||
this.handle_start_debugging_request(request, session.clone(), window, cx);
|
||||
|
@ -300,7 +286,7 @@ impl DebugPanel {
|
|||
|
||||
let serialized_layout = persistence::get_serialized_pane_layout(adapter_name).await;
|
||||
|
||||
let workspace = this.update_in(cx, |this, window, cx| {
|
||||
let (debug_session, workspace) = this.update_in(cx, |this, window, cx| {
|
||||
this.sessions.retain(|session| {
|
||||
session
|
||||
.read(cx)
|
||||
|
@ -311,7 +297,7 @@ impl DebugPanel {
|
|||
.is_terminated()
|
||||
});
|
||||
|
||||
let session_item = DebugSession::running(
|
||||
let debug_session = DebugSession::running(
|
||||
this.project.clone(),
|
||||
this.workspace.clone(),
|
||||
session,
|
||||
|
@ -324,20 +310,62 @@ impl DebugPanel {
|
|||
// We might want to make this an event subscription and only notify when a new thread is selected
|
||||
// This is used to filter the command menu correctly
|
||||
cx.observe(
|
||||
&session_item.read(cx).running_state().clone(),
|
||||
&debug_session.read(cx).running_state().clone(),
|
||||
|_, _, cx| cx.notify(),
|
||||
)
|
||||
.detach();
|
||||
|
||||
this.sessions.push(session_item.clone());
|
||||
this.activate_session(session_item, window, cx);
|
||||
this.workspace.clone()
|
||||
this.sessions.push(debug_session.clone());
|
||||
this.activate_session(debug_session.clone(), window, cx);
|
||||
|
||||
(debug_session, this.workspace.clone())
|
||||
})?;
|
||||
|
||||
workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.focus_panel::<Self>(window, cx);
|
||||
})?;
|
||||
Ok(())
|
||||
|
||||
Ok(debug_session)
|
||||
}
|
||||
|
||||
fn handle_restart_request(
|
||||
&mut self,
|
||||
mut curr_session: Entity<Session>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
while let Some(parent_session) =
|
||||
curr_session.read_with(cx, |session, _| session.parent_session().cloned())
|
||||
{
|
||||
curr_session = parent_session;
|
||||
}
|
||||
|
||||
let Some(worktree) = curr_session.read(cx).worktree() else {
|
||||
log::error!("Attempted to start a child session from non local debug session");
|
||||
return;
|
||||
};
|
||||
|
||||
let dap_store_handle = self.project.read(cx).dap_store().clone();
|
||||
let label = curr_session.read(cx).label().clone();
|
||||
let adapter = curr_session.read(cx).adapter().clone();
|
||||
let binary = curr_session.read(cx).binary().clone();
|
||||
let task = curr_session.update(cx, |session, cx| session.shutdown(cx));
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
task.await;
|
||||
|
||||
let (session, task) = dap_store_handle.update(cx, |dap_store, cx| {
|
||||
let session = dap_store.new_session(label, adapter, None, cx);
|
||||
|
||||
let task = session.update(cx, |session, cx| {
|
||||
session.boot(binary, worktree, dap_store_handle.downgrade(), cx)
|
||||
});
|
||||
(session, task)
|
||||
})?;
|
||||
Self::register_session(this, session, cx).await?;
|
||||
task.await
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
||||
pub fn handle_start_debugging_request(
|
||||
|
@ -353,40 +381,23 @@ impl DebugPanel {
|
|||
};
|
||||
|
||||
let dap_store_handle = self.project.read(cx).dap_store().clone();
|
||||
let definition = parent_session.read(cx).definition().clone();
|
||||
let label = parent_session.read(cx).label().clone();
|
||||
let adapter = parent_session.read(cx).adapter().clone();
|
||||
let mut binary = parent_session.read(cx).binary().clone();
|
||||
binary.request_args = request.clone();
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let (session, task) = dap_store_handle.update(cx, |dap_store, cx| {
|
||||
let session =
|
||||
dap_store.new_session(definition.clone(), Some(parent_session.clone()), cx);
|
||||
dap_store.new_session(label, adapter, Some(parent_session.clone()), cx);
|
||||
|
||||
let task = session.update(cx, |session, cx| {
|
||||
session.boot(binary, worktree, dap_store_handle.downgrade(), cx)
|
||||
});
|
||||
(session, task)
|
||||
})?;
|
||||
|
||||
match task.await {
|
||||
Err(e) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace.show_error(&e, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
|
||||
session
|
||||
.update(cx, |session, cx| session.shutdown(cx))?
|
||||
.await;
|
||||
}
|
||||
Ok(_) => Self::register_session(this, session, cx).await?,
|
||||
}
|
||||
|
||||
anyhow::Ok(())
|
||||
Self::register_session(this, session, cx).await?;
|
||||
task.await
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
|
@ -394,127 +405,6 @@ impl DebugPanel {
|
|||
pub fn active_session(&self) -> Option<Entity<DebugSession>> {
|
||||
self.active_session.clone()
|
||||
}
|
||||
|
||||
pub fn resolve_scenario(
|
||||
&self,
|
||||
scenario: DebugScenario,
|
||||
task_context: TaskContext,
|
||||
buffer: Option<Entity<Buffer>>,
|
||||
window: &Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Task<Result<DebugTaskDefinition>> {
|
||||
let project = self.project.read(cx);
|
||||
let dap_store = project.dap_store().downgrade();
|
||||
let task_store = project.task_store().downgrade();
|
||||
let workspace = self.workspace.clone();
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
let DebugScenario {
|
||||
adapter,
|
||||
label,
|
||||
build,
|
||||
request,
|
||||
initialize_args,
|
||||
tcp_connection,
|
||||
stop_on_entry,
|
||||
} = scenario;
|
||||
let request = if let Some(mut request) = request {
|
||||
if let DebugRequest::Launch(launch_config) = &mut request {
|
||||
let mut variable_names = HashMap::default();
|
||||
let mut substituted_variables = HashSet::default();
|
||||
let task_variables = task_context
|
||||
.task_variables
|
||||
.iter()
|
||||
.map(|(key, value)| {
|
||||
let key_string = key.to_string();
|
||||
if !variable_names.contains_key(&key_string) {
|
||||
variable_names.insert(key_string.clone(), key.clone());
|
||||
}
|
||||
(key_string, value.as_str())
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
let cwd = launch_config
|
||||
.cwd
|
||||
.as_ref()
|
||||
.and_then(|cwd| cwd.to_str())
|
||||
.and_then(|cwd| {
|
||||
task::substitute_all_template_variables_in_str(
|
||||
cwd,
|
||||
&task_variables,
|
||||
&variable_names,
|
||||
&mut substituted_variables,
|
||||
)
|
||||
});
|
||||
|
||||
if let Some(cwd) = cwd {
|
||||
launch_config.cwd = Some(PathBuf::from(cwd))
|
||||
}
|
||||
|
||||
if let Some(program) = task::substitute_all_template_variables_in_str(
|
||||
&launch_config.program,
|
||||
&task_variables,
|
||||
&variable_names,
|
||||
&mut substituted_variables,
|
||||
) {
|
||||
launch_config.program = program;
|
||||
}
|
||||
|
||||
for arg in launch_config.args.iter_mut() {
|
||||
if let Some(substituted_arg) =
|
||||
task::substitute_all_template_variables_in_str(
|
||||
&arg,
|
||||
&task_variables,
|
||||
&variable_names,
|
||||
&mut substituted_variables,
|
||||
)
|
||||
{
|
||||
*arg = substituted_arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
request
|
||||
} else if let Some(build) = build {
|
||||
let Some(task) = task_store.update(cx, |this, cx| {
|
||||
this.task_inventory().and_then(|inventory| {
|
||||
inventory
|
||||
.read(cx)
|
||||
.task_template_by_label(buffer, &build, cx)
|
||||
})
|
||||
})?
|
||||
else {
|
||||
anyhow::bail!("Couldn't find task template for {:?}", build)
|
||||
};
|
||||
let Some(task) = task.resolve_task("debug-build-task", &task_context) else {
|
||||
anyhow::bail!("Could not resolve task variables within a debug scenario");
|
||||
};
|
||||
|
||||
let run_build = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.spawn_in_terminal(task.resolved.clone(), window, cx)
|
||||
})?;
|
||||
|
||||
let exit_status = run_build.await.transpose()?.context("task cancelled")?;
|
||||
if !exit_status.success() {
|
||||
anyhow::bail!("Build failed");
|
||||
}
|
||||
|
||||
dap_store
|
||||
.update(cx, |this, cx| this.run_debug_locator(task.resolved, cx))?
|
||||
.await?
|
||||
} else {
|
||||
return Err(anyhow!("No request or build provided"));
|
||||
};
|
||||
Ok(DebugTaskDefinition {
|
||||
label,
|
||||
adapter,
|
||||
request,
|
||||
initialize_args,
|
||||
stop_on_entry,
|
||||
tcp_connection,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn close_session(&mut self, entity_id: EntityId, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(session) = self
|
||||
.sessions
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue