Detailed changes
@@ -88,7 +88,7 @@ impl<'a> From<&'a str> for DebugAdapterName {
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
pub struct TcpArguments {
pub host: Ipv4Addr,
pub port: u16,
@@ -127,7 +127,7 @@ impl TcpArguments {
)]
pub struct DebugTaskDefinition {
pub label: SharedString,
- pub adapter: SharedString,
+ pub adapter: DebugAdapterName,
pub request: DebugRequest,
/// Additional initialization arguments to be sent on DAP initialization
pub initialize_args: Option<serde_json::Value>,
@@ -153,7 +153,7 @@ impl DebugTaskDefinition {
pub fn to_scenario(&self) -> DebugScenario {
DebugScenario {
label: self.label.clone(),
- adapter: self.adapter.clone(),
+ adapter: self.adapter.clone().into(),
build: None,
request: Some(self.request.clone()),
stop_on_entry: self.stop_on_entry,
@@ -207,7 +207,7 @@ impl DebugTaskDefinition {
.map(TcpArgumentsTemplate::from_proto)
.transpose()?,
stop_on_entry: proto.stop_on_entry,
- adapter: proto.adapter.into(),
+ adapter: DebugAdapterName(proto.adapter.into()),
request: match request {
proto::debug_task_definition::Request::DebugAttachRequest(config) => {
DebugRequest::Attach(AttachRequest {
@@ -229,7 +229,7 @@ impl DebugTaskDefinition {
}
/// Created from a [DebugTaskDefinition], this struct describes how to spawn the debugger to create a previously-configured debug session.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
pub struct DebugAdapterBinary {
pub command: String,
pub arguments: Vec<String>,
@@ -568,11 +568,11 @@ impl DapLogView {
.sessions()
.filter_map(|session| {
let session = session.read(cx);
- session.adapter_name();
+ session.adapter();
let client = session.adapter_client()?;
Some(DapMenuItem {
client_id: client.id(),
- client_name: session.adapter_name().to_string(),
+ client_name: session.adapter().to_string(),
has_adapter_logs: client.has_adapter_logs(),
selected_entry: self.current_view.map_or(LogKind::Adapter, |(_, kind)| kind),
})
@@ -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,59 +198,65 @@ impl DebugPanel {
})
}
- fn start_from_definition(
+ pub fn start_session(
&mut self,
- definition: DebugTaskDefinition,
+ scenario: DebugScenario,
+ task_context: TaskContext,
+ active_buffer: Option<Entity<Buffer>>,
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);
+ ) {
+ 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,
+ )
})
- .ok();
- })
- .ok();
+ })?
+ .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(())
})
- }
-
- pub fn start_session(
- &mut self,
- scenario: DebugScenario,
- task_context: TaskContext,
- active_buffer: Option<Entity<Buffer>>,
- 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
- })
.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
@@ -4,7 +4,10 @@ use std::{
path::{Path, PathBuf},
};
-use dap::{DapRegistry, DebugRequest, adapters::DebugTaskDefinition};
+use dap::{
+ DapRegistry, DebugRequest,
+ adapters::{DebugAdapterName, DebugTaskDefinition},
+};
use editor::{Editor, EditorElement, EditorStyle};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
@@ -13,9 +16,9 @@ use gpui::{
};
use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch};
use project::{TaskSourceKind, task_store::TaskStore};
-use session_modes::{AttachMode, DebugScenarioDelegate, LaunchMode};
use settings::Settings;
use task::{DebugScenario, LaunchRequest};
+use tasks_ui::task_contexts;
use theme::ThemeSettings;
use ui::{
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context,
@@ -36,7 +39,7 @@ pub(super) struct NewSessionModal {
mode: NewSessionMode,
stop_on_entry: ToggleState,
initialize_args: Option<serde_json::Value>,
- debugger: Option<SharedString>,
+ debugger: Option<DebugAdapterName>,
last_selected_profile_name: Option<SharedString>,
}
@@ -143,16 +146,19 @@ impl NewSessionModal {
let debug_panel = self.debug_panel.clone();
let workspace = self.workspace.clone();
-
cx.spawn_in(window, async move |this, cx| {
let task_contexts = workspace
- .update_in(cx, |workspace, window, cx| {
- tasks_ui::task_contexts(workspace, window, cx)
- })?
+ .update_in(cx, |this, window, cx| task_contexts(this, window, cx))?
.await;
-
- let task_context = task_contexts.active_context().cloned().unwrap_or_default();
-
+ let task_context = task_contexts
+ .active_item_context
+ .map(|(_, _, context)| context)
+ .or_else(|| {
+ task_contexts
+ .active_worktree_context
+ .map(|(_, context)| context)
+ })
+ .unwrap_or_default();
debug_panel.update_in(cx, |debug_panel, window, cx| {
debug_panel.start_session(config, task_context, None, window, cx)
})?;
@@ -167,18 +173,17 @@ impl NewSessionModal {
fn update_attach_picker(
attach: &Entity<AttachMode>,
- selected_debugger: &str,
+ adapter: &DebugAdapterName,
window: &mut Window,
cx: &mut App,
) {
attach.update(cx, |this, cx| {
- if selected_debugger != this.definition.adapter.as_ref() {
- let adapter: SharedString = selected_debugger.to_owned().into();
+ if adapter != &this.definition.adapter {
this.definition.adapter = adapter.clone();
this.attach_picker.update(cx, |this, cx| {
this.picker.update(cx, |this, cx| {
- this.delegate.definition.adapter = adapter;
+ this.delegate.definition.adapter = adapter.clone();
this.focus(window, cx);
})
});
@@ -194,15 +199,16 @@ impl NewSessionModal {
) -> ui::DropdownMenu {
let workspace = self.workspace.clone();
let weak = cx.weak_entity();
- let debugger = self.debugger.clone();
+ let label = self
+ .debugger
+ .as_ref()
+ .map(|d| d.0.clone())
+ .unwrap_or_else(|| SELECT_DEBUGGER_LABEL.clone());
DropdownMenu::new(
"dap-adapter-picker",
- debugger
- .as_ref()
- .unwrap_or_else(|| &SELECT_DEBUGGER_LABEL)
- .clone(),
+ label,
ContextMenu::build(window, cx, move |mut menu, _, cx| {
- let setter_for_name = |name: SharedString| {
+ let setter_for_name = |name: DebugAdapterName| {
let weak = weak.clone();
move |window: &mut Window, cx: &mut App| {
weak.update(cx, |this, cx| {
@@ -222,7 +228,7 @@ impl NewSessionModal {
.unwrap_or_default();
for adapter in available_adapters {
- menu = menu.entry(adapter.0.clone(), None, setter_for_name(adapter.0.clone()));
+ menu = menu.entry(adapter.0.clone(), None, setter_for_name(adapter.clone()));
}
menu
}),
@@ -251,7 +257,7 @@ impl NewSessionModal {
move |window: &mut Window, cx: &mut App| {
weak.update(cx, |this, cx| {
this.last_selected_profile_name = Some(SharedString::from(&task.label));
- this.debugger = Some(task.adapter.clone());
+ this.debugger = Some(DebugAdapterName(task.adapter.clone()));
this.initialize_args = task.initialize_args.clone();
match &task.request {
Some(DebugRequest::Launch(launch_config)) => {
@@ -374,7 +380,7 @@ impl NewSessionMode {
}
fn attach(
- debugger: Option<SharedString>,
+ debugger: Option<DebugAdapterName>,
workspace: Entity<Workspace>,
window: &mut Window,
cx: &mut Context<NewSessionModal>,
@@ -431,41 +437,6 @@ impl Focusable for NewSessionMode {
}
}
-impl RenderOnce for LaunchMode {
- fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
- v_flex()
- .p_2()
- .w_full()
- .gap_3()
- .track_focus(&self.program.focus_handle(cx))
- .child(
- div().child(
- Label::new("Program")
- .size(ui::LabelSize::Small)
- .color(Color::Muted),
- ),
- )
- .child(render_editor(&self.program, window, cx))
- .child(
- div().child(
- Label::new("Working Directory")
- .size(ui::LabelSize::Small)
- .color(Color::Muted),
- ),
- )
- .child(render_editor(&self.cwd, window, cx))
- }
-}
-
-impl RenderOnce for AttachMode {
- fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
- v_flex()
- .w_full()
- .track_focus(&self.attach_picker.focus_handle(cx))
- .child(self.attach_picker.clone())
- }
-}
-
impl RenderOnce for NewSessionMode {
fn render(self, window: &mut Window, cx: &mut App) -> impl ui::IntoElement {
match self {
@@ -684,318 +655,342 @@ impl Focusable for NewSessionModal {
impl ModalView for NewSessionModal {}
-// This module makes sure that the modes setup the correct subscriptions whenever they're created
-mod session_modes {
- use std::rc::Rc;
-
- use super::*;
+impl RenderOnce for LaunchMode {
+ fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
+ v_flex()
+ .p_2()
+ .w_full()
+ .gap_3()
+ .track_focus(&self.program.focus_handle(cx))
+ .child(
+ div().child(
+ Label::new("Program")
+ .size(ui::LabelSize::Small)
+ .color(Color::Muted),
+ ),
+ )
+ .child(render_editor(&self.program, window, cx))
+ .child(
+ div().child(
+ Label::new("Working Directory")
+ .size(ui::LabelSize::Small)
+ .color(Color::Muted),
+ ),
+ )
+ .child(render_editor(&self.cwd, window, cx))
+ }
+}
- #[derive(Clone)]
- #[non_exhaustive]
- pub(super) struct LaunchMode {
- pub(super) program: Entity<Editor>,
- pub(super) cwd: Entity<Editor>,
+impl RenderOnce for AttachMode {
+ fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
+ v_flex()
+ .w_full()
+ .track_focus(&self.attach_picker.focus_handle(cx))
+ .child(self.attach_picker.clone())
}
+}
+
+use std::rc::Rc;
- impl LaunchMode {
- pub(super) fn new(
- past_launch_config: Option<LaunchRequest>,
- window: &mut Window,
- cx: &mut App,
- ) -> Entity<Self> {
- let (past_program, past_cwd) = past_launch_config
- .map(|config| (Some(config.program), config.cwd))
- .unwrap_or_else(|| (None, None));
+#[derive(Clone)]
+pub(super) struct LaunchMode {
+ program: Entity<Editor>,
+ cwd: Entity<Editor>,
+}
- let program = cx.new(|cx| Editor::single_line(window, cx));
- program.update(cx, |this, cx| {
- this.set_placeholder_text("Program path", cx);
+impl LaunchMode {
+ pub(super) fn new(
+ past_launch_config: Option<LaunchRequest>,
+ window: &mut Window,
+ cx: &mut App,
+ ) -> Entity<Self> {
+ let (past_program, past_cwd) = past_launch_config
+ .map(|config| (Some(config.program), config.cwd))
+ .unwrap_or_else(|| (None, None));
- if let Some(past_program) = past_program {
- this.set_text(past_program, window, cx);
- };
- });
- let cwd = cx.new(|cx| Editor::single_line(window, cx));
- cwd.update(cx, |this, cx| {
- this.set_placeholder_text("Working Directory", cx);
- if let Some(past_cwd) = past_cwd {
- this.set_text(past_cwd.to_string_lossy(), window, cx);
- };
- });
- cx.new(|_| Self { program, cwd })
- }
+ let program = cx.new(|cx| Editor::single_line(window, cx));
+ program.update(cx, |this, cx| {
+ this.set_placeholder_text("Program path", cx);
- pub(super) fn debug_task(&self, cx: &App) -> task::LaunchRequest {
- let path = self.cwd.read(cx).text(cx);
- task::LaunchRequest {
- program: self.program.read(cx).text(cx),
- cwd: path.is_empty().not().then(|| PathBuf::from(path)),
- args: Default::default(),
- env: Default::default(),
- }
+ if let Some(past_program) = past_program {
+ this.set_text(past_program, window, cx);
+ };
+ });
+ let cwd = cx.new(|cx| Editor::single_line(window, cx));
+ cwd.update(cx, |this, cx| {
+ this.set_placeholder_text("Working Directory", cx);
+ if let Some(past_cwd) = past_cwd {
+ this.set_text(past_cwd.to_string_lossy(), window, cx);
+ };
+ });
+ cx.new(|_| Self { program, cwd })
+ }
+
+ pub(super) fn debug_task(&self, cx: &App) -> task::LaunchRequest {
+ let path = self.cwd.read(cx).text(cx);
+ task::LaunchRequest {
+ program: self.program.read(cx).text(cx),
+ cwd: path.is_empty().not().then(|| PathBuf::from(path)),
+ args: Default::default(),
+ env: Default::default(),
}
}
+}
- #[derive(Clone)]
- pub(super) struct AttachMode {
- pub(super) definition: DebugTaskDefinition,
- pub(super) attach_picker: Entity<AttachModal>,
- _subscription: Rc<Subscription>,
- }
-
- impl AttachMode {
- pub(super) fn new(
- debugger: Option<SharedString>,
- workspace: Entity<Workspace>,
- window: &mut Window,
- cx: &mut Context<NewSessionModal>,
- ) -> Entity<Self> {
- let definition = DebugTaskDefinition {
- adapter: debugger.clone().unwrap_or_default(),
- label: "Attach New Session Setup".into(),
- request: dap::DebugRequest::Attach(task::AttachRequest { process_id: None }),
- initialize_args: None,
- tcp_connection: None,
- stop_on_entry: Some(false),
- };
- let attach_picker = cx.new(|cx| {
- let modal = AttachModal::new(definition.clone(), workspace, false, window, cx);
- window.focus(&modal.focus_handle(cx));
+#[derive(Clone)]
+pub(super) struct AttachMode {
+ pub(super) definition: DebugTaskDefinition,
+ pub(super) attach_picker: Entity<AttachModal>,
+ _subscription: Rc<Subscription>,
+}
- modal
- });
+impl AttachMode {
+ pub(super) fn new(
+ debugger: Option<DebugAdapterName>,
+ workspace: Entity<Workspace>,
+ window: &mut Window,
+ cx: &mut Context<NewSessionModal>,
+ ) -> Entity<Self> {
+ let definition = DebugTaskDefinition {
+ adapter: debugger.unwrap_or(DebugAdapterName("".into())),
+ label: "Attach New Session Setup".into(),
+ request: dap::DebugRequest::Attach(task::AttachRequest { process_id: None }),
+ initialize_args: None,
+ tcp_connection: None,
+ stop_on_entry: Some(false),
+ };
+ let attach_picker = cx.new(|cx| {
+ let modal = AttachModal::new(definition.clone(), workspace, false, window, cx);
+ window.focus(&modal.focus_handle(cx));
- let subscription = cx.subscribe(&attach_picker, |_, _, _, cx| {
- cx.emit(DismissEvent);
- });
+ modal
+ });
- cx.new(|_| Self {
- definition,
- attach_picker,
- _subscription: Rc::new(subscription),
- })
- }
- pub(super) fn debug_task(&self) -> task::AttachRequest {
- task::AttachRequest { process_id: None }
- }
+ let subscription = cx.subscribe(&attach_picker, |_, _, _, cx| {
+ cx.emit(DismissEvent);
+ });
+
+ cx.new(|_| Self {
+ definition,
+ attach_picker,
+ _subscription: Rc::new(subscription),
+ })
}
+ pub(super) fn debug_task(&self) -> task::AttachRequest {
+ task::AttachRequest { process_id: None }
+ }
+}
- pub(super) struct DebugScenarioDelegate {
- task_store: Entity<TaskStore>,
- candidates: Option<Vec<(TaskSourceKind, DebugScenario)>>,
- selected_index: usize,
- matches: Vec<StringMatch>,
- prompt: String,
+pub(super) struct DebugScenarioDelegate {
+ task_store: Entity<TaskStore>,
+ candidates: Option<Vec<(TaskSourceKind, DebugScenario)>>,
+ selected_index: usize,
+ matches: Vec<StringMatch>,
+ prompt: String,
+ debug_panel: WeakEntity<DebugPanel>,
+ workspace: WeakEntity<Workspace>,
+}
+
+impl DebugScenarioDelegate {
+ pub(super) fn new(
debug_panel: WeakEntity<DebugPanel>,
workspace: WeakEntity<Workspace>,
+ task_store: Entity<TaskStore>,
+ ) -> Self {
+ Self {
+ task_store,
+ candidates: None,
+ selected_index: 0,
+ matches: Vec::new(),
+ prompt: String::new(),
+ debug_panel,
+ workspace,
+ }
}
+}
- impl DebugScenarioDelegate {
- pub(super) fn new(
- debug_panel: WeakEntity<DebugPanel>,
- workspace: WeakEntity<Workspace>,
- task_store: Entity<TaskStore>,
- ) -> Self {
- Self {
- task_store,
- candidates: None,
- selected_index: 0,
- matches: Vec::new(),
- prompt: String::new(),
- debug_panel,
- workspace,
- }
- }
+impl PickerDelegate for DebugScenarioDelegate {
+ type ListItem = ui::ListItem;
+
+ fn match_count(&self) -> usize {
+ self.matches.len()
}
- impl PickerDelegate for DebugScenarioDelegate {
- type ListItem = ui::ListItem;
+ fn selected_index(&self) -> usize {
+ self.selected_index
+ }
- fn match_count(&self) -> usize {
- self.matches.len()
- }
+ fn set_selected_index(
+ &mut self,
+ ix: usize,
+ _window: &mut Window,
+ _cx: &mut Context<picker::Picker<Self>>,
+ ) {
+ self.selected_index = ix;
+ }
- fn selected_index(&self) -> usize {
- self.selected_index
- }
+ fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> std::sync::Arc<str> {
+ "".into()
+ }
- fn set_selected_index(
- &mut self,
- ix: usize,
- _window: &mut Window,
- _cx: &mut Context<picker::Picker<Self>>,
- ) {
- self.selected_index = ix;
- }
+ fn update_matches(
+ &mut self,
+ query: String,
+ window: &mut Window,
+ cx: &mut Context<picker::Picker<Self>>,
+ ) -> gpui::Task<()> {
+ let candidates: Vec<_> = match &self.candidates {
+ Some(candidates) => candidates
+ .into_iter()
+ .enumerate()
+ .map(|(index, (_, candidate))| {
+ StringMatchCandidate::new(index, candidate.label.as_ref())
+ })
+ .collect(),
+ None => {
+ let worktree_ids: Vec<_> = self
+ .workspace
+ .update(cx, |this, cx| {
+ this.visible_worktrees(cx)
+ .map(|tree| tree.read(cx).id())
+ .collect()
+ })
+ .ok()
+ .unwrap_or_default();
- fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> std::sync::Arc<str> {
- "".into()
- }
+ let scenarios: Vec<_> = self
+ .task_store
+ .read(cx)
+ .task_inventory()
+ .map(|item| item.read(cx).list_debug_scenarios(worktree_ids.into_iter()))
+ .unwrap_or_default();
- fn update_matches(
- &mut self,
- query: String,
- window: &mut Window,
- cx: &mut Context<picker::Picker<Self>>,
- ) -> gpui::Task<()> {
- let candidates: Vec<_> = match &self.candidates {
- Some(candidates) => candidates
+ self.candidates = Some(scenarios.clone());
+
+ scenarios
.into_iter()
.enumerate()
.map(|(index, (_, candidate))| {
StringMatchCandidate::new(index, candidate.label.as_ref())
})
- .collect(),
- None => {
- let worktree_ids: Vec<_> = self
- .workspace
- .update(cx, |this, cx| {
- this.visible_worktrees(cx)
- .map(|tree| tree.read(cx).id())
- .collect()
- })
- .ok()
- .unwrap_or_default();
-
- let scenarios: Vec<_> = self
- .task_store
- .read(cx)
- .task_inventory()
- .map(|item| item.read(cx).list_debug_scenarios(worktree_ids.into_iter()))
- .unwrap_or_default();
-
- self.candidates = Some(scenarios.clone());
-
- scenarios
- .into_iter()
- .enumerate()
- .map(|(index, (_, candidate))| {
- StringMatchCandidate::new(index, candidate.label.as_ref())
- })
- .collect()
- }
- };
+ .collect()
+ }
+ };
- cx.spawn_in(window, async move |picker, cx| {
- let matches = fuzzy::match_strings(
- &candidates,
- &query,
- true,
- 1000,
- &Default::default(),
- cx.background_executor().clone(),
- )
- .await;
+ cx.spawn_in(window, async move |picker, cx| {
+ let matches = fuzzy::match_strings(
+ &candidates,
+ &query,
+ true,
+ 1000,
+ &Default::default(),
+ cx.background_executor().clone(),
+ )
+ .await;
- picker
- .update(cx, |picker, _| {
- let delegate = &mut picker.delegate;
-
- delegate.matches = matches;
- delegate.prompt = query;
-
- if delegate.matches.is_empty() {
- delegate.selected_index = 0;
- } else {
- delegate.selected_index =
- delegate.selected_index.min(delegate.matches.len() - 1);
- }
- })
- .log_err();
- })
- }
+ picker
+ .update(cx, |picker, _| {
+ let delegate = &mut picker.delegate;
- fn confirm(
- &mut self,
- _: bool,
- window: &mut Window,
- cx: &mut Context<picker::Picker<Self>>,
- ) {
- let debug_scenario =
- self.matches
- .get(self.selected_index())
- .and_then(|match_candidate| {
- self.candidates
- .as_ref()
- .map(|candidates| candidates[match_candidate.candidate_id].clone())
- });
-
- let Some((task_source_kind, debug_scenario)) = debug_scenario else {
- return;
- };
+ delegate.matches = matches;
+ delegate.prompt = query;
- let task_context = if let TaskSourceKind::Worktree {
- id: worktree_id,
- directory_in_worktree: _,
- id_base: _,
- } = task_source_kind
- {
- let workspace = self.workspace.clone();
-
- cx.spawn_in(window, async move |_, cx| {
- workspace
- .update_in(cx, |workspace, window, cx| {
- tasks_ui::task_contexts(workspace, window, cx)
- })
- .ok()?
- .await
- .task_context_for_worktree_id(worktree_id)
- .cloned()
+ if delegate.matches.is_empty() {
+ delegate.selected_index = 0;
+ } else {
+ delegate.selected_index =
+ delegate.selected_index.min(delegate.matches.len() - 1);
+ }
})
- } else {
- gpui::Task::ready(None)
- };
+ .log_err();
+ })
+ }
- cx.spawn_in(window, async move |this, cx| {
- let task_context = task_context.await.unwrap_or_default();
+ fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
+ let debug_scenario = self
+ .matches
+ .get(self.selected_index())
+ .and_then(|match_candidate| {
+ self.candidates
+ .as_ref()
+ .map(|candidates| candidates[match_candidate.candidate_id].clone())
+ });
- this.update_in(cx, |this, window, cx| {
- this.delegate
- .debug_panel
- .update(cx, |panel, cx| {
- panel.start_session(debug_scenario, task_context, None, window, cx);
- })
- .ok();
+ let Some((task_source_kind, debug_scenario)) = debug_scenario else {
+ return;
+ };
- cx.emit(DismissEvent);
- })
- .ok();
+ let task_context = if let TaskSourceKind::Worktree {
+ id: worktree_id,
+ directory_in_worktree: _,
+ id_base: _,
+ } = task_source_kind
+ {
+ let workspace = self.workspace.clone();
+
+ cx.spawn_in(window, async move |_, cx| {
+ workspace
+ .update_in(cx, |workspace, window, cx| {
+ tasks_ui::task_contexts(workspace, window, cx)
+ })
+ .ok()?
+ .await
+ .task_context_for_worktree_id(worktree_id)
+ .cloned()
})
- .detach();
- }
+ } else {
+ gpui::Task::ready(None)
+ };
- fn dismissed(&mut self, _: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
- cx.emit(DismissEvent);
- }
+ cx.spawn_in(window, async move |this, cx| {
+ let task_context = task_context.await.unwrap_or_default();
- fn render_match(
- &self,
- ix: usize,
- selected: bool,
- window: &mut Window,
- cx: &mut Context<picker::Picker<Self>>,
- ) -> Option<Self::ListItem> {
- let hit = &self.matches[ix];
-
- let highlighted_location = HighlightedMatch {
- text: hit.string.clone(),
- highlight_positions: hit.positions.clone(),
- char_count: hit.string.chars().count(),
- color: Color::Default,
- };
+ this.update_in(cx, |this, window, cx| {
+ this.delegate
+ .debug_panel
+ .update(cx, |panel, cx| {
+ panel.start_session(debug_scenario, task_context, None, window, cx);
+ })
+ .ok();
- let icon = Icon::new(IconName::FileTree)
- .color(Color::Muted)
- .size(ui::IconSize::Small);
-
- Some(
- ListItem::new(SharedString::from(format!("debug-scenario-selection-{ix}")))
- .inset(true)
- .start_slot::<Icon>(icon)
- .spacing(ListItemSpacing::Sparse)
- .toggle_state(selected)
- .child(highlighted_location.render(window, cx)),
- )
- }
+ cx.emit(DismissEvent);
+ })
+ .ok();
+ })
+ .detach();
+ }
+
+ fn dismissed(&mut self, _: &mut Window, cx: &mut Context<picker::Picker<Self>>) {
+ cx.emit(DismissEvent);
+ }
+
+ fn render_match(
+ &self,
+ ix: usize,
+ selected: bool,
+ window: &mut Window,
+ cx: &mut Context<picker::Picker<Self>>,
+ ) -> Option<Self::ListItem> {
+ let hit = &self.matches[ix];
+
+ let highlighted_location = HighlightedMatch {
+ text: hit.string.clone(),
+ highlight_positions: hit.positions.clone(),
+ char_count: hit.string.chars().count(),
+ color: Color::Default,
+ };
+
+ let icon = Icon::new(IconName::FileTree)
+ .color(Color::Muted)
+ .size(ui::IconSize::Small);
+
+ Some(
+ ListItem::new(SharedString::from(format!("debug-scenario-selection-{ix}")))
+ .inset(true)
+ .start_slot::<Icon>(icon)
+ .spacing(ListItemSpacing::Sparse)
+ .toggle_state(selected)
+ .child(highlighted_location.render(window, cx)),
+ )
}
}
@@ -1,5 +1,5 @@
use collections::HashMap;
-use dap::Capabilities;
+use dap::{Capabilities, adapters::DebugAdapterName};
use db::kvp::KEY_VALUE_STORE;
use gpui::{Axis, Context, Entity, EntityId, Focusable, Subscription, WeakEntity, Window};
use project::Project;
@@ -90,7 +90,7 @@ pub(crate) struct SerializedPane {
const DEBUGGER_PANEL_PREFIX: &str = "debugger_panel_";
pub(crate) async fn serialize_pane_layout(
- adapter_name: SharedString,
+ adapter_name: DebugAdapterName,
pane_group: SerializedPaneLayout,
) -> anyhow::Result<()> {
if let Ok(serialized_pane_group) = serde_json::to_string(&pane_group) {
@@ -15,7 +15,9 @@ use breakpoint_list::BreakpointList;
use collections::{HashMap, IndexMap};
use console::Console;
use dap::{
- Capabilities, RunInTerminalRequestArguments, Thread, client::SessionId,
+ Capabilities, RunInTerminalRequestArguments, Thread,
+ adapters::{DebugAdapterName, DebugTaskDefinition},
+ client::SessionId,
debugger_settings::DebuggerSettings,
};
use futures::{SinkExt, channel::mpsc};
@@ -23,6 +25,7 @@ use gpui::{
Action as _, AnyView, AppContext, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
NoAction, Pixels, Point, Subscription, Task, WeakEntity,
};
+use language::Buffer;
use loaded_source_list::LoadedSourceList;
use module_list::ModuleList;
use project::{
@@ -34,6 +37,10 @@ use rpc::proto::ViewId;
use serde_json::Value;
use settings::Settings;
use stack_frame_list::StackFrameList;
+use task::{
+ DebugScenario, LaunchRequest, TaskContext, substitute_variables_in_map,
+ substitute_variables_in_str,
+};
use terminal_view::TerminalView;
use ui::{
ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, ContextMenu,
@@ -667,6 +674,143 @@ impl RunningState {
self.panes.pane_at_pixel_position(position).is_some()
}
+ pub(crate) fn resolve_scenario(
+ &self,
+ scenario: DebugScenario,
+ task_context: TaskContext,
+ buffer: Option<Entity<Buffer>>,
+ window: &Window,
+ cx: &mut Context<Self>,
+ ) -> Task<Result<DebugTaskDefinition>> {
+ let Some(workspace) = self.workspace.upgrade() else {
+ return Task::ready(Err(anyhow!("no workspace")));
+ };
+ let project = workspace.read(cx).project().clone();
+ let dap_store = project.read(cx).dap_store().downgrade();
+ let task_store = project.read(cx).task_store().downgrade();
+ let weak_project = project.downgrade();
+ let weak_workspace = workspace.downgrade();
+ cx.spawn_in(window, async move |this, cx| {
+ let DebugScenario {
+ adapter,
+ label,
+ build,
+ request,
+ initialize_args,
+ tcp_connection,
+ stop_on_entry,
+ } = scenario;
+ let request = if let Some(request) = request {
+ 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 terminal = project
+ .update_in(cx, |project, window, cx| {
+ project.create_terminal(
+ TerminalKind::Task(task.resolved.clone()),
+ window.window_handle(),
+ cx,
+ )
+ })?
+ .await?;
+
+ let terminal_view = cx.new_window_entity(|window, cx| {
+ TerminalView::new(
+ terminal.clone(),
+ weak_workspace,
+ None,
+ weak_project,
+ false,
+ window,
+ cx,
+ )
+ })?;
+
+ this.update_in(cx, |this, window, cx| {
+ this.ensure_pane_item(DebuggerPaneItem::Terminal, window, cx);
+ this.debug_terminal.update(cx, |debug_terminal, cx| {
+ debug_terminal.terminal = Some(terminal_view);
+ cx.notify();
+ });
+ })?;
+
+ let exit_status = terminal
+ .read_with(cx, |terminal, cx| terminal.wait_for_completed_task(cx))?
+ .await
+ .ok_or_else(|| anyhow!("Failed to wait for completed task"))?;
+
+ 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"));
+ };
+ let request = match request {
+ dap::DebugRequest::Launch(launch_request) => {
+ let cwd = match launch_request.cwd.as_deref().and_then(|path| path.to_str()) {
+ Some(cwd) => {
+ let substituted_cwd = substitute_variables_in_str(&cwd, &task_context)
+ .ok_or_else(|| anyhow!("Failed to substitute variables in cwd"))?;
+ Some(PathBuf::from(substituted_cwd))
+ }
+ None => None,
+ };
+
+ let env = substitute_variables_in_map(
+ &launch_request.env.into_iter().collect(),
+ &task_context,
+ )
+ .ok_or_else(|| anyhow!("Failed to substitute variables in env"))?
+ .into_iter()
+ .collect();
+ let new_launch_request = LaunchRequest {
+ program: substitute_variables_in_str(
+ &launch_request.program,
+ &task_context,
+ )
+ .ok_or_else(|| anyhow!("Failed to substitute variables in program"))?,
+ args: launch_request
+ .args
+ .into_iter()
+ .map(|arg| substitute_variables_in_str(&arg, &task_context))
+ .collect::<Option<Vec<_>>>()
+ .ok_or_else(|| anyhow!("Failed to substitute variables in args"))?,
+ cwd,
+ env,
+ };
+
+ dap::DebugRequest::Launch(new_launch_request)
+ }
+ request @ dap::DebugRequest::Attach(_) => request,
+ };
+ Ok(DebugTaskDefinition {
+ label,
+ adapter: DebugAdapterName(adapter),
+ request,
+ initialize_args,
+ stop_on_entry,
+ tcp_connection,
+ })
+ })
+ }
+
fn handle_run_in_terminal(
&self,
request: &RunInTerminalRequestArguments,
@@ -914,7 +1058,7 @@ impl RunningState {
let Some((adapter_name, pane_group)) = this
.update(cx, |this, cx| {
- let adapter_name = this.session.read(cx).adapter_name();
+ let adapter_name = this.session.read(cx).adapter();
(
adapter_name,
persistence::build_serialized_pane_layout(&this.panes.root, cx),
@@ -444,11 +444,13 @@ async fn test_handle_start_debugging_request(
.read(cx)
.session(cx);
let parent_session = active_session.read(cx).parent_session().unwrap();
+ let mut original_binary = parent_session.read(cx).binary().clone();
+ original_binary.request_args = StartDebuggingRequestArguments {
+ request: StartDebuggingRequestArgumentsRequest::Launch,
+ configuration: fake_config.clone(),
+ };
- assert_eq!(
- active_session.read(cx).definition(),
- parent_session.read(cx).definition()
- );
+ assert_eq!(active_session.read(cx).binary(), &original_binary);
})
.unwrap();
@@ -50,7 +50,7 @@ use std::{
sync::{Arc, Once},
};
use task::{DebugScenario, SpawnInTerminal};
-use util::ResultExt as _;
+use util::{ResultExt as _, merge_json_value_into};
use worktree::Worktree;
#[derive(Debug)]
@@ -393,7 +393,8 @@ impl DapStore {
pub fn new_session(
&mut self,
- template: DebugTaskDefinition,
+ label: SharedString,
+ adapter: DebugAdapterName,
parent_session: Option<Entity<Session>>,
cx: &mut Context<Self>,
) -> Entity<Session> {
@@ -409,7 +410,8 @@ impl DapStore {
self.breakpoint_store.clone(),
session_id,
parent_session,
- template.clone(),
+ label,
+ adapter,
cx,
);
@@ -435,6 +437,7 @@ impl DapStore {
pub fn boot_session(
&self,
session: Entity<Session>,
+ definition: DebugTaskDefinition,
cx: &mut Context<Self>,
) -> Task<Result<()>> {
let Some(worktree) = self.worktree_store.read(cx).visible_worktrees(cx).next() else {
@@ -442,17 +445,20 @@ impl DapStore {
};
let dap_store = cx.weak_entity();
- let definition = session.read(cx).definition();
cx.spawn({
let session = session.clone();
async move |this, cx| {
- let binary = this
+ let mut binary = this
.update(cx, |this, cx| {
this.get_debug_adapter_binary(definition.clone(), cx)
})?
.await?;
+ if let Some(args) = definition.initialize_args {
+ merge_json_value_into(args, &mut binary.request_args.configuration);
+ }
+
session
.update(cx, |session, cx| {
session.boot(binary, worktree, dap_store, cx)
@@ -12,7 +12,7 @@ use super::dap_command::{
use super::dap_store::DapStore;
use anyhow::{Context as _, Result, anyhow};
use collections::{HashMap, HashSet, IndexMap, IndexSet};
-use dap::adapters::{DebugAdapterBinary, DebugTaskDefinition};
+use dap::adapters::{DebugAdapterBinary, DebugAdapterName};
use dap::messages::Response;
use dap::requests::{Request, RunInTerminal, StartDebugging};
use dap::{
@@ -32,7 +32,7 @@ use gpui::{
Task, WeakEntity,
};
-use serde_json::{Value, json};
+use serde_json::Value;
use smol::stream::StreamExt;
use std::any::TypeId;
use std::collections::BTreeMap;
@@ -45,7 +45,7 @@ use std::{
sync::Arc,
};
use text::{PointUtf16, ToPointUtf16};
-use util::{ResultExt, merge_json_value_into};
+use util::ResultExt;
use worktree::Worktree;
#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Ord, Eq)]
@@ -307,18 +307,11 @@ impl LocalMode {
fn initialize_sequence(
&self,
capabilities: &Capabilities,
- definition: &DebugTaskDefinition,
initialized_rx: oneshot::Receiver<()>,
dap_store: WeakEntity<DapStore>,
- breakpoint_store: Entity<BreakpointStore>,
cx: &App,
) -> Task<Result<()>> {
- let mut raw = self.binary.request_args.clone();
-
- merge_json_value_into(
- definition.initialize_args.clone().unwrap_or(json!({})),
- &mut raw.configuration,
- );
+ let raw = self.binary.request_args.clone();
// Of relevance: https://github.com/microsoft/vscode/issues/4902#issuecomment-368583522
let launch = match raw.request {
@@ -349,6 +342,8 @@ impl LocalMode {
let worktree = self.worktree().clone();
let configuration_sequence = cx.spawn({
async move |cx| {
+ let breakpoint_store =
+ dap_store.update(cx, |dap_store, _| dap_store.breakpoint_store().clone())?;
initialized_rx.await?;
let errors_by_path = cx
.update(|cx| this.send_source_breakpoints(false, &breakpoint_store, cx))?
@@ -505,9 +500,10 @@ pub struct OutputToken(pub usize);
/// Represents a current state of a single debug adapter and provides ways to mutate it.
pub struct Session {
pub mode: Mode,
- definition: DebugTaskDefinition,
- pub(super) capabilities: Capabilities,
id: SessionId,
+ label: SharedString,
+ adapter: DebugAdapterName,
+ pub(super) capabilities: Capabilities,
child_session_ids: HashSet<SessionId>,
parent_session: Option<Entity<Session>>,
modules: Vec<dap::Module>,
@@ -636,7 +632,8 @@ impl Session {
breakpoint_store: Entity<BreakpointStore>,
session_id: SessionId,
parent_session: Option<Entity<Session>>,
- template: DebugTaskDefinition,
+ label: SharedString,
+ adapter: DebugAdapterName,
cx: &mut App,
) -> Entity<Self> {
cx.new::<Self>(|cx| {
@@ -685,7 +682,8 @@ impl Session {
ignore_breakpoints: false,
breakpoint_store,
exception_breakpoints: Default::default(),
- definition: template,
+ label,
+ adapter,
};
this
@@ -805,16 +803,12 @@ impl Session {
&local_mode.binary
}
- pub fn adapter_name(&self) -> SharedString {
- self.definition.adapter.clone()
+ pub fn adapter(&self) -> DebugAdapterName {
+ self.adapter.clone()
}
pub fn label(&self) -> SharedString {
- self.definition.label.clone()
- }
-
- pub fn definition(&self) -> DebugTaskDefinition {
- self.definition.clone()
+ self.label.clone()
}
pub fn is_terminated(&self) -> bool {
@@ -943,7 +937,7 @@ impl Session {
}
pub(super) fn request_initialize(&mut self, cx: &mut Context<Self>) -> Task<Result<()>> {
- let adapter_id = String::from(self.definition.adapter.clone());
+ let adapter_id = self.adapter().to_string();
let request = Initialize { adapter_id };
match &self.mode {
Mode::Running(local_mode) => {
@@ -983,14 +977,9 @@ impl Session {
cx: &mut Context<Self>,
) -> Task<Result<()>> {
match &self.mode {
- Mode::Running(local_mode) => local_mode.initialize_sequence(
- &self.capabilities,
- &self.definition,
- initialize_rx,
- dap_store,
- self.breakpoint_store.clone(),
- cx,
- ),
+ Mode::Running(local_mode) => {
+ local_mode.initialize_sequence(&self.capabilities, initialize_rx, dap_store, cx)
+ }
Mode::Building => Task::ready(Err(anyhow!("cannot initialize, still building"))),
}
}
@@ -66,7 +66,7 @@ use image_store::{ImageItemEvent, ImageStoreEvent};
use ::git::{blame::Blame, status::FileStatus};
use gpui::{
AnyEntity, App, AppContext, AsyncApp, BorrowAppContext, Context, Entity, EventEmitter, Hsla,
- SharedString, Task, WeakEntity, Window, prelude::FluentBuilder,
+ SharedString, Task, WeakEntity, Window,
};
use itertools::Itertools;
use language::{
@@ -3565,10 +3565,9 @@ impl Project {
) -> Task<anyhow::Result<Vec<InlayHint>>> {
let snapshot = buffer_handle.read(cx).snapshot();
- let Some(inline_value_provider) = session
- .read(cx)
- .adapter_name()
- .map(|adapter_name| DapRegistry::global(cx).adapter(&adapter_name))
+ let adapter = session.read(cx).adapter();
+ let Some(inline_value_provider) = DapRegistry::global(cx)
+ .adapter(&adapter)
.and_then(|adapter| adapter.inline_value_provider())
else {
return Task::ready(Err(anyhow::anyhow!("Inline value provider not found")));
@@ -20,7 +20,8 @@ pub use debug_format::{
};
pub use task_template::{
DebugArgsRequest, HideStrategy, RevealStrategy, TaskTemplate, TaskTemplates,
- substitute_all_template_variables_in_str,
+ substitute_all_template_variables_in_str, substitute_variables_in_map,
+ substitute_variables_in_str,
};
pub use vscode_debug_format::VsCodeDebugTaskFile;
pub use vscode_format::VsCodeTaskFile;
@@ -293,6 +293,28 @@ fn to_hex_hash(object: impl Serialize) -> anyhow::Result<String> {
Ok(hex::encode(hasher.finalize()))
}
+pub fn substitute_variables_in_str(template_str: &str, context: &TaskContext) -> Option<String> {
+ let mut variable_names = HashMap::default();
+ let mut substituted_variables = HashSet::default();
+ let task_variables = context
+ .task_variables
+ .0
+ .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<_, _>>();
+ substitute_all_template_variables_in_str(
+ template_str,
+ &task_variables,
+ &variable_names,
+ &mut substituted_variables,
+ )
+}
pub fn substitute_all_template_variables_in_str<A: AsRef<str>>(
template_str: &str,
task_variables: &HashMap<String, A>,
@@ -349,6 +371,31 @@ fn substitute_all_template_variables_in_vec(
Some(expanded)
}
+pub fn substitute_variables_in_map(
+ keys_and_values: &HashMap<String, String>,
+ context: &TaskContext,
+) -> Option<HashMap<String, String>> {
+ let mut variable_names = HashMap::default();
+ let mut substituted_variables = HashSet::default();
+ let task_variables = context
+ .task_variables
+ .0
+ .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<_, _>>();
+ substitute_all_template_variables_in_map(
+ keys_and_values,
+ &task_variables,
+ &variable_names,
+ &mut substituted_variables,
+ )
+}
fn substitute_all_template_variables_in_map(
keys_and_values: &HashMap<String, String>,
task_variables: &HashMap<String, &str>,