diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index c88df28b99..9d1b040f56 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -34,7 +34,7 @@ "ctrl-q": "zed::Quit", "f4": "debugger::Start", "shift-f5": "debugger::Stop", - "ctrl-shift-f5": "debugger::Restart", + "ctrl-shift-f5": "debugger::RerunSession", "f6": "debugger::Pause", "f7": "debugger::StepOver", "ctrl-f11": "debugger::StepInto", @@ -598,7 +598,7 @@ // "foo-bar": ["task::Spawn", { "task_name": "MyTask", "reveal_target": "dock" }] // or by tag: // "foo-bar": ["task::Spawn", { "task_tag": "MyTag" }], - "f5": "debugger::RerunLastSession" + "f5": "debugger::Rerun" } }, { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 7d6ce6e80a..3a4cbcfacc 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -5,7 +5,7 @@ "bindings": { "f4": "debugger::Start", "shift-f5": "debugger::Stop", - "shift-cmd-f5": "debugger::Restart", + "shift-cmd-f5": "debugger::RerunSession", "f6": "debugger::Pause", "f7": "debugger::StepOver", "f11": "debugger::StepInto", @@ -652,7 +652,7 @@ "cmd-k shift-up": "workspace::SwapPaneUp", "cmd-k shift-down": "workspace::SwapPaneDown", "cmd-shift-x": "zed::Extensions", - "f5": "debugger::RerunLastSession" + "f5": "debugger::Rerun" } }, { diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 795b4caf9e..cb48083192 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -5,7 +5,7 @@ use crate::session::running::breakpoint_list::BreakpointList; use crate::{ ClearAllBreakpoints, Continue, CopyDebugAdapterArguments, Detach, FocusBreakpointList, FocusConsole, FocusFrames, FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, - NewProcessModal, NewProcessMode, Pause, Restart, StepInto, StepOut, StepOver, Stop, + NewProcessModal, NewProcessMode, Pause, RerunSession, StepInto, StepOut, StepOver, Stop, ToggleExpandItem, ToggleSessionPicker, ToggleThreadPicker, persistence, spawn_task_or_modal, }; use anyhow::{Context as _, Result, anyhow}; @@ -25,7 +25,7 @@ use gpui::{ use itertools::Itertools as _; use language::Buffer; use project::debugger::session::{Session, SessionStateEvent}; -use project::{Fs, ProjectPath, WorktreeId}; +use project::{DebugScenarioContext, Fs, ProjectPath, WorktreeId}; use project::{Project, debugger::session::ThreadStatus}; use rpc::proto::{self}; use settings::Settings; @@ -197,6 +197,7 @@ impl DebugPanel { .and_then(|buffer| buffer.read(cx).file()) .map(|f| f.worktree_id(cx)) }); + let Some(worktree) = worktree .and_then(|id| self.project.read(cx).worktree_for_id(id, cx)) .or_else(|| self.project.read(cx).visible_worktrees(cx).next()) @@ -204,6 +205,7 @@ impl DebugPanel { log::debug!("Could not find a worktree to spawn the debug session in"); return; }; + self.debug_scenario_scheduled_last = true; if let Some(inventory) = self .project @@ -214,7 +216,15 @@ impl DebugPanel { .cloned() { inventory.update(cx, |inventory, _| { - inventory.scenario_scheduled(scenario.clone()); + inventory.scenario_scheduled( + scenario.clone(), + // todo(debugger): Task context is cloned three times + // once in Session,inventory, and in resolve scenario + // we should wrap it in an RC instead to save some memory + task_context.clone(), + worktree_id, + active_buffer.as_ref().map(|buffer| buffer.downgrade()), + ); }) } let task = cx.spawn_in(window, { @@ -225,6 +235,16 @@ impl DebugPanel { let definition = debug_session .update_in(cx, |debug_session, window, cx| { debug_session.running_state().update(cx, |running, cx| { + if scenario.build.is_some() { + running.scenario = Some(scenario.clone()); + running.scenario_context = Some(DebugScenarioContext { + active_buffer: active_buffer + .as_ref() + .map(|entity| entity.downgrade()), + task_context: task_context.clone(), + worktree_id: worktree_id, + }); + }; running.resolve_scenario( scenario, task_context, @@ -273,7 +293,8 @@ impl DebugPanel { return; }; let workspace = self.workspace.clone(); - let Some(scenario) = task_inventory.read(cx).last_scheduled_scenario().cloned() else { + let Some((scenario, context)) = task_inventory.read(cx).last_scheduled_scenario().cloned() + else { window.defer(cx, move |window, cx| { workspace .update(cx, |workspace, cx| { @@ -284,28 +305,22 @@ impl DebugPanel { return; }; - 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) - })? - .await; + let DebugScenarioContext { + task_context, + worktree_id, + active_buffer, + } = context; - let task_context = task_contexts.active_context().cloned().unwrap_or_default(); - let worktree_id = task_contexts.worktree(); + let active_buffer = active_buffer.and_then(|buffer| buffer.upgrade()); - this.update_in(cx, |this, window, cx| { - this.start_session( - scenario.clone(), - task_context, - None, - worktree_id, - window, - cx, - ); - }) - }) - .detach(); + self.start_session( + scenario, + task_context, + active_buffer, + worktree_id, + window, + cx, + ); } pub(crate) async fn register_session( @@ -758,16 +773,16 @@ impl DebugPanel { .icon_size(IconSize::XSmall) .on_click(window.listener_for( &running_state, - |this, _, _window, cx| { - this.restart_session(cx); + |this, _, window, cx| { + this.rerun_session(window, cx); }, )) .tooltip({ let focus_handle = focus_handle.clone(); move |window, cx| { Tooltip::for_action_in( - "Restart", - &Restart, + "Rerun Session", + &RerunSession, &focus_handle, window, cx, @@ -1600,12 +1615,13 @@ impl workspace::DebuggerProvider for DebuggerProvider { definition: DebugScenario, context: TaskContext, buffer: Option>, + worktree_id: Option, window: &mut Window, cx: &mut App, ) { self.0.update(cx, |_, cx| { - cx.defer_in(window, |this, window, cx| { - this.start_session(definition, context, buffer, None, window, cx); + cx.defer_in(window, move |this, window, cx| { + this.start_session(definition, context, buffer, worktree_id, window, cx); }) }) } diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index ade1308f06..71b3ce1a31 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -37,6 +37,7 @@ actions!( Detach, Pause, Restart, + RerunSession, StepInto, StepOver, StepOut, @@ -54,7 +55,8 @@ actions!( ShowStackTrace, ToggleThreadPicker, ToggleSessionPicker, - RerunLastSession, + #[action(deprecated_aliases = ["debugger::RerunLastSession"])] + Rerun, ToggleExpandItem, ] ); @@ -74,17 +76,15 @@ pub fn init(cx: &mut App) { .register_action(|workspace: &mut Workspace, _: &Start, window, cx| { NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx); }) - .register_action( - |workspace: &mut Workspace, _: &RerunLastSession, window, cx| { - let Some(debug_panel) = workspace.panel::(cx) else { - return; - }; + .register_action(|workspace: &mut Workspace, _: &Rerun, window, cx| { + let Some(debug_panel) = workspace.panel::(cx) else { + return; + }; - debug_panel.update(cx, |debug_panel, cx| { - debug_panel.rerun_last_session(workspace, window, cx); - }) - }, - ) + debug_panel.update(cx, |debug_panel, cx| { + debug_panel.rerun_last_session(workspace, window, cx); + }) + }) .register_action( |workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| { workspace.project().update(cx, |project, cx| { @@ -210,6 +210,14 @@ pub fn init(cx: &mut App) { .ok(); } }) + .on_action({ + let active_item = active_item.clone(); + move |_: &RerunSession, window, cx| { + active_item + .update(cx, |item, cx| item.rerun_session(window, cx)) + .ok(); + } + }) .on_action({ let active_item = active_item.clone(); move |_: &Stop, _, cx| { diff --git a/crates/debugger_ui/src/new_process_modal.rs b/crates/debugger_ui/src/new_process_modal.rs index 126eadbd92..e857e33677 100644 --- a/crates/debugger_ui/src/new_process_modal.rs +++ b/crates/debugger_ui/src/new_process_modal.rs @@ -23,7 +23,9 @@ use gpui::{ }; use itertools::Itertools as _; use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch}; -use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore}; +use project::{ + DebugScenarioContext, ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore, +}; use settings::{Settings, initial_local_debug_tasks_content}; use task::{DebugScenario, RevealTarget, ZedDebugConfig}; use theme::ThemeSettings; @@ -92,6 +94,7 @@ impl NewProcessModal { cx.spawn_in(window, async move |workspace, cx| { let task_contexts = workspace.update_in(cx, |workspace, window, cx| { + // todo(debugger): get the buffer here (if the active item is an editor) and store it so we can pass it to start_session later tasks_ui::task_contexts(workspace, window, cx) })?; workspace.update_in(cx, |workspace, window, cx| { @@ -1110,7 +1113,11 @@ pub(super) struct TaskMode { pub(super) struct DebugDelegate { task_store: Entity, - candidates: Vec<(Option, DebugScenario)>, + candidates: Vec<( + Option, + DebugScenario, + Option, + )>, selected_index: usize, matches: Vec, prompt: String, @@ -1208,7 +1215,11 @@ impl DebugDelegate { this.delegate.candidates = recent .into_iter() - .map(|scenario| Self::get_scenario_kind(&languages, &dap_registry, scenario)) + .map(|(scenario, context)| { + let (kind, scenario) = + Self::get_scenario_kind(&languages, &dap_registry, scenario); + (kind, scenario, Some(context)) + }) .chain( scenarios .into_iter() @@ -1223,7 +1234,7 @@ impl DebugDelegate { .map(|(kind, scenario)| { let (language, scenario) = Self::get_scenario_kind(&languages, &dap_registry, scenario); - (language.or(Some(kind)), scenario) + (language.or(Some(kind)), scenario, None) }), ) .collect(); @@ -1269,7 +1280,7 @@ impl PickerDelegate for DebugDelegate { let candidates: Vec<_> = candidates .into_iter() .enumerate() - .map(|(index, (_, candidate))| { + .map(|(index, (_, candidate, _))| { StringMatchCandidate::new(index, candidate.label.as_ref()) }) .collect(); @@ -1434,25 +1445,40 @@ impl PickerDelegate for DebugDelegate { .get(self.selected_index()) .and_then(|match_candidate| self.candidates.get(match_candidate.candidate_id).cloned()); - let Some((_, debug_scenario)) = debug_scenario else { + let Some((_, debug_scenario, context)) = debug_scenario else { return; }; - let (task_context, worktree_id) = self - .task_contexts - .as_ref() - .and_then(|task_contexts| { - Some(( - task_contexts.active_context().cloned()?, - task_contexts.worktree(), - )) - }) - .unwrap_or_default(); + let context = context.unwrap_or_else(|| { + self.task_contexts + .as_ref() + .and_then(|task_contexts| { + Some(DebugScenarioContext { + task_context: task_contexts.active_context().cloned()?, + active_buffer: None, + worktree_id: task_contexts.worktree(), + }) + }) + .unwrap_or_default() + }); + let DebugScenarioContext { + task_context, + active_buffer, + worktree_id, + } = context; + let active_buffer = active_buffer.and_then(|buffer| buffer.upgrade()); send_telemetry(&debug_scenario, TelemetrySpawnLocation::ScenarioList, cx); self.debug_panel .update(cx, |panel, cx| { - panel.start_session(debug_scenario, task_context, None, worktree_id, window, cx); + panel.start_session( + debug_scenario, + task_context, + active_buffer, + worktree_id, + window, + cx, + ); }) .ok(); diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 91e49059e9..b9f373daa4 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -33,7 +33,7 @@ use language::Buffer; use loaded_source_list::LoadedSourceList; use module_list::ModuleList; use project::{ - Project, WorktreeId, + DebugScenarioContext, Project, WorktreeId, debugger::session::{Session, SessionEvent, ThreadId, ThreadStatus}, terminals::TerminalKind, }; @@ -79,6 +79,8 @@ pub struct RunningState { pane_close_subscriptions: HashMap, dock_axis: Axis, _schedule_serialize: Option>, + pub(crate) scenario: Option, + pub(crate) scenario_context: Option, } impl RunningState { @@ -831,6 +833,8 @@ impl RunningState { debug_terminal, dock_axis, _schedule_serialize: None, + scenario: None, + scenario_context: None, } } @@ -1039,7 +1043,7 @@ impl RunningState { let scenario = dap_registry .adapter(&adapter) .with_context(|| anyhow!("{}: is not a valid adapter name", &adapter))?.config_from_zed_format(zed_config) -.await?; + .await?; config = scenario.config; util::merge_non_null_json_value_into(extra_config, &mut config); @@ -1525,6 +1529,34 @@ impl RunningState { }); } + pub fn rerun_session(&mut self, window: &mut Window, cx: &mut Context) { + if let Some((scenario, context)) = self.scenario.take().zip(self.scenario_context.take()) + && scenario.build.is_some() + { + let DebugScenarioContext { + task_context, + active_buffer, + worktree_id, + } = context; + let active_buffer = active_buffer.and_then(|buffer| buffer.upgrade()); + + self.workspace + .update(cx, |workspace, cx| { + workspace.start_debug_session( + scenario, + task_context, + active_buffer, + worktree_id, + window, + cx, + ) + }) + .ok(); + } else { + self.restart_session(cx); + } + } + pub fn restart_session(&self, cx: &mut Context) { self.session().update(cx, |state, cx| { state.restart(None, cx); diff --git a/crates/debugger_ui/src/tests.rs b/crates/debugger_ui/src/tests.rs index 0828f13714..ac3fdf1f18 100644 --- a/crates/debugger_ui/src/tests.rs +++ b/crates/debugger_ui/src/tests.rs @@ -115,6 +115,7 @@ pub fn start_debug_session_with) + 'static>( config.to_scenario(), TaskContext::default(), None, + None, window, cx, ) diff --git a/crates/debugger_ui/src/tests/new_process_modal.rs b/crates/debugger_ui/src/tests/new_process_modal.rs index eb8c7f8063..81c5f7b598 100644 --- a/crates/debugger_ui/src/tests/new_process_modal.rs +++ b/crates/debugger_ui/src/tests/new_process_modal.rs @@ -141,7 +141,14 @@ async fn test_debug_session_substitutes_variables_and_relativizes_paths( workspace .update(cx, |workspace, window, cx| { - workspace.start_debug_session(scenario, task_context.clone(), None, window, cx) + workspace.start_debug_session( + scenario, + task_context.clone(), + None, + None, + window, + cx, + ) }) .unwrap(); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index fe904ab4ec..baa8e1a21c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -6186,7 +6186,14 @@ impl Editor { workspace.update(cx, |workspace, cx| { dap::send_telemetry(&scenario, TelemetrySpawnLocation::Gutter, cx); - workspace.start_debug_session(scenario, context, Some(buffer), window, cx); + workspace.start_debug_session( + scenario, + context, + Some(buffer), + None, + window, + cx, + ); }); Some(Task::ready(Ok(()))) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 060e7c0415..ee144b8b36 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -131,7 +131,8 @@ pub use language::Location; #[cfg(any(test, feature = "test-support"))] pub use prettier::FORMAT_SUFFIX as TEST_PRETTIER_FORMAT_SUFFIX; pub use task_inventory::{ - BasicContextProvider, ContextProviderWithTasks, Inventory, TaskContexts, TaskSourceKind, + BasicContextProvider, ContextProviderWithTasks, DebugScenarioContext, Inventory, TaskContexts, + TaskSourceKind, }; pub use buffer_store::ProjectTransaction; diff --git a/crates/project/src/task_inventory.rs b/crates/project/src/task_inventory.rs index 51399bd0ef..d0f1c71daf 100644 --- a/crates/project/src/task_inventory.rs +++ b/crates/project/src/task_inventory.rs @@ -12,7 +12,7 @@ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use dap::DapRegistry; use fs::Fs; -use gpui::{App, AppContext as _, Context, Entity, SharedString, Task}; +use gpui::{App, AppContext as _, Context, Entity, SharedString, Task, WeakEntity}; use itertools::Itertools; use language::{ Buffer, ContextLocation, ContextProvider, File, Language, LanguageToolchainStore, Location, @@ -31,11 +31,18 @@ use worktree::WorktreeId; use crate::{task_store::TaskSettingsLocation, worktree_store::WorktreeStore}; +#[derive(Clone, Debug, Default)] +pub struct DebugScenarioContext { + pub task_context: TaskContext, + pub worktree_id: Option, + pub active_buffer: Option>, +} + /// Inventory tracks available tasks for a given project. pub struct Inventory { fs: Arc, last_scheduled_tasks: VecDeque<(TaskSourceKind, ResolvedTask)>, - last_scheduled_scenarios: VecDeque, + last_scheduled_scenarios: VecDeque<(DebugScenario, DebugScenarioContext)>, templates_from_settings: InventoryFor, scenarios_from_settings: InventoryFor, } @@ -245,16 +252,29 @@ impl Inventory { }) } - pub fn scenario_scheduled(&mut self, scenario: DebugScenario) { + pub fn scenario_scheduled( + &mut self, + scenario: DebugScenario, + task_context: TaskContext, + worktree_id: Option, + active_buffer: Option>, + ) { self.last_scheduled_scenarios - .retain(|s| s.label != scenario.label); - self.last_scheduled_scenarios.push_back(scenario); + .retain(|(s, _)| s.label != scenario.label); + self.last_scheduled_scenarios.push_back(( + scenario, + DebugScenarioContext { + task_context, + worktree_id, + active_buffer, + }, + )); if self.last_scheduled_scenarios.len() > 5_000 { self.last_scheduled_scenarios.pop_front(); } } - pub fn last_scheduled_scenario(&self) -> Option<&DebugScenario> { + pub fn last_scheduled_scenario(&self) -> Option<&(DebugScenario, DebugScenarioContext)> { self.last_scheduled_scenarios.back() } @@ -265,7 +285,10 @@ impl Inventory { current_resolved_tasks: Vec<(TaskSourceKind, task::ResolvedTask)>, add_current_language_tasks: bool, cx: &mut App, - ) -> Task<(Vec, Vec<(TaskSourceKind, DebugScenario)>)> { + ) -> Task<( + Vec<(DebugScenario, DebugScenarioContext)>, + Vec<(TaskSourceKind, DebugScenario)>, + )> { let mut scenarios = Vec::new(); if let Some(worktree_id) = task_contexts @@ -765,7 +788,7 @@ impl Inventory { } } } - self.last_scheduled_scenarios.retain_mut(|scenario| { + self.last_scheduled_scenarios.retain_mut(|(scenario, _)| { if !previously_existing_scenarios.contains(&scenario.label) { return true; } @@ -1304,7 +1327,7 @@ mod tests { .clone(); inventory.update(cx, |this, _| { - this.scenario_scheduled(scenario.clone()); + this.scenario_scheduled(scenario.clone(), TaskContext::default(), None, None); }); assert_eq!( @@ -1316,7 +1339,8 @@ mod tests { .0 .first() .unwrap() - .clone(), + .clone() + .0, scenario ); @@ -1346,6 +1370,7 @@ mod tests { .0 .first() .unwrap() + .0 .adapter, "Delve", ); @@ -1367,15 +1392,14 @@ mod tests { .unwrap(); }); - assert_eq!( + assert!( inventory .update(cx, |this, cx| { this.list_debug_scenarios(&TaskContexts::default(), vec![], vec![], false, cx) }) .await .0 - .first(), - None + .is_empty(), ); } diff --git a/crates/workspace/src/tasks.rs b/crates/workspace/src/tasks.rs index 4134e7ed74..26edbd8d03 100644 --- a/crates/workspace/src/tasks.rs +++ b/crates/workspace/src/tasks.rs @@ -3,7 +3,7 @@ use std::process::ExitStatus; use anyhow::Result; use gpui::{AppContext, Context, Entity, Task}; use language::Buffer; -use project::TaskSourceKind; +use project::{TaskSourceKind, WorktreeId}; use remote::ConnectionState; use task::{DebugScenario, ResolvedTask, SpawnInTerminal, TaskContext, TaskTemplate}; use ui::Window; @@ -95,11 +95,19 @@ impl Workspace { scenario: DebugScenario, task_context: TaskContext, active_buffer: Option>, + worktree_id: Option, window: &mut Window, cx: &mut Context, ) { if let Some(provider) = self.debugger_provider.as_mut() { - provider.start_session(scenario, task_context, active_buffer, window, cx) + provider.start_session( + scenario, + task_context, + active_buffer, + worktree_id, + window, + cx, + ) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7c5355bfd1..3100abcca7 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -146,6 +146,7 @@ pub trait DebuggerProvider { definition: DebugScenario, task_context: TaskContext, active_buffer: Option>, + worktree_id: Option, window: &mut Window, cx: &mut App, );