diff --git a/assets/settings/initial_debug_tasks.json b/assets/settings/initial_debug_tasks.json index 67fc4fdedb..75e42b2d1b 100644 --- a/assets/settings/initial_debug_tasks.json +++ b/assets/settings/initial_debug_tasks.json @@ -1,3 +1,7 @@ +// Some example tasks for common languages. +// +// For more documentation on how to configure debug tasks, +// see: https://zed.dev/docs/debugger [ { "label": "Debug active PHP file", diff --git a/assets/settings/initial_local_debug_tasks.json b/assets/settings/initial_local_debug_tasks.json new file mode 100644 index 0000000000..4be1a903ab --- /dev/null +++ b/assets/settings/initial_local_debug_tasks.json @@ -0,0 +1,5 @@ +// Project-local debug tasks +// +// For more documentation on how to configure debug tasks, +// see: https://zed.dev/docs/debugger +[] diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index dfd317480a..01f0ad7289 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -50,6 +50,7 @@ project.workspace = true rpc.workspace = true serde.workspace = true serde_json.workspace = true +# serde_json_lenient.workspace = true settings.workspace = true shlex.workspace = true sysinfo.workspace = true diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 9374fc7282..ef0e476d1a 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -7,7 +7,7 @@ use crate::{ ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, ToggleSessionPicker, ToggleThreadPicker, persistence, spawn_task_or_modal, }; -use anyhow::{Context as _, Result, anyhow}; +use anyhow::Result; use command_palette_hooks::CommandPaletteFilter; use dap::StartDebuggingRequestArguments; use dap::adapters::DebugAdapterName; @@ -24,7 +24,7 @@ use gpui::{ use language::Buffer; use project::debugger::session::{Session, SessionStateEvent}; -use project::{Fs, ProjectPath, WorktreeId}; +use project::{Fs, WorktreeId}; use project::{Project, debugger::session::ThreadStatus}; use rpc::proto::{self}; use settings::Settings; @@ -942,68 +942,69 @@ impl DebugPanel { cx.notify(); } - pub(crate) fn save_scenario( - &self, - scenario: &DebugScenario, - worktree_id: WorktreeId, - window: &mut Window, - cx: &mut App, - ) -> Task> { - self.workspace - .update(cx, |workspace, cx| { - let Some(mut path) = workspace.absolute_path_of_worktree(worktree_id, cx) else { - return Task::ready(Err(anyhow!("Couldn't get worktree path"))); - }; + // TODO: restore once we have proper comment preserving file edits + // pub(crate) fn save_scenario( + // &self, + // scenario: &DebugScenario, + // worktree_id: WorktreeId, + // window: &mut Window, + // cx: &mut App, + // ) -> Task> { + // self.workspace + // .update(cx, |workspace, cx| { + // let Some(mut path) = workspace.absolute_path_of_worktree(worktree_id, cx) else { + // return Task::ready(Err(anyhow!("Couldn't get worktree path"))); + // }; - let serialized_scenario = serde_json::to_value(scenario); + // let serialized_scenario = serde_json::to_value(scenario); - cx.spawn_in(window, async move |workspace, cx| { - let serialized_scenario = serialized_scenario?; - let fs = - workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?; + // cx.spawn_in(window, async move |workspace, cx| { + // let serialized_scenario = serialized_scenario?; + // let fs = + // workspace.read_with(cx, |workspace, _| workspace.app_state().fs.clone())?; - path.push(paths::local_settings_folder_relative_path()); - if !fs.is_dir(path.as_path()).await { - fs.create_dir(path.as_path()).await?; - } - path.pop(); + // path.push(paths::local_settings_folder_relative_path()); + // if !fs.is_dir(path.as_path()).await { + // fs.create_dir(path.as_path()).await?; + // } + // path.pop(); - path.push(paths::local_debug_file_relative_path()); - let path = path.as_path(); + // path.push(paths::local_debug_file_relative_path()); + // let path = path.as_path(); - if !fs.is_file(path).await { - let content = - serde_json::to_string_pretty(&serde_json::Value::Array(vec![ - serialized_scenario, - ]))?; + // if !fs.is_file(path).await { + // fs.create_file(path, Default::default()).await?; + // fs.write( + // path, + // initial_local_debug_tasks_content().to_string().as_bytes(), + // ) + // .await?; + // } - fs.create_file(path, Default::default()).await?; - fs.save(path, &content.into(), Default::default()).await?; - } else { - let content = fs.load(path).await?; - let mut values = serde_json::from_str::>(&content)?; - values.push(serialized_scenario); - fs.save( - path, - &serde_json::to_string_pretty(&values).map(Into::into)?, - Default::default(), - ) - .await?; - } + // let content = fs.load(path).await?; + // let mut values = + // serde_json_lenient::from_str::>(&content)?; + // values.push(serialized_scenario); + // fs.save( + // path, + // &serde_json_lenient::to_string_pretty(&values).map(Into::into)?, + // Default::default(), + // ) + // .await?; - workspace.update(cx, |workspace, cx| { - workspace - .project() - .read(cx) - .project_path_for_absolute_path(&path, cx) - .context( - "Couldn't get project path for .zed/debug.json in active worktree", - ) - })? - }) - }) - .unwrap_or_else(|err| Task::ready(Err(err))) - } + // workspace.update(cx, |workspace, cx| { + // workspace + // .project() + // .read(cx) + // .project_path_for_absolute_path(&path, cx) + // .context( + // "Couldn't get project path for .zed/debug.json in active worktree", + // ) + // })? + // }) + // }) + // .unwrap_or_else(|err| Task::ready(Err(err))) + // } pub(crate) fn toggle_thread_picker(&mut self, window: &mut Window, cx: &mut Context) { self.thread_picker_menu_handle.toggle(window, cx); diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 43c7678797..106c66ebae 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -3,7 +3,7 @@ use debugger_panel::{DebugPanel, ToggleFocus}; use editor::Editor; use feature_flags::{DebuggerFeatureFlag, FeatureFlagViewExt}; use gpui::{App, EntityInputHandler, actions}; -use new_session_modal::{NewSessionModal, NewSessionMode}; +use new_process_modal::{NewProcessModal, NewProcessMode}; use project::debugger::{self, breakpoint_store::SourceBreakpoint}; use session::DebugSession; use settings::Settings; @@ -15,7 +15,7 @@ use workspace::{ItemHandle, ShutdownDebugAdapters, Workspace}; pub mod attach_modal; pub mod debugger_panel; mod dropdown_menus; -mod new_session_modal; +mod new_process_modal; mod persistence; pub(crate) mod session; mod stack_trace_view; @@ -210,7 +210,7 @@ pub fn init(cx: &mut App) { }, ) .register_action(|workspace: &mut Workspace, _: &Start, window, cx| { - NewSessionModal::show(workspace, window, NewSessionMode::Launch, None, cx); + NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx); }) .register_action( |workspace: &mut Workspace, _: &RerunLastSession, window, cx| { @@ -352,7 +352,7 @@ fn spawn_task_or_modal( .detach_and_log_err(cx) } Spawn::ViaModal { reveal_target } => { - NewSessionModal::show(workspace, window, NewSessionMode::Task, *reveal_target, cx); + NewProcessModal::show(workspace, window, NewProcessMode::Task, *reveal_target, cx); } } } diff --git a/crates/debugger_ui/src/new_session_modal.rs b/crates/debugger_ui/src/new_process_modal.rs similarity index 67% rename from crates/debugger_ui/src/new_session_modal.rs rename to crates/debugger_ui/src/new_process_modal.rs index 115d03ba48..84bddf36a4 100644 --- a/crates/debugger_ui/src/new_session_modal.rs +++ b/crates/debugger_ui/src/new_process_modal.rs @@ -1,11 +1,10 @@ use collections::FxHashMap; -use language::{LanguageRegistry, Point, Selection}; +use language::LanguageRegistry; +use paths::local_debug_file_relative_path; use std::{ borrow::Cow, - ops::Not, path::{Path, PathBuf}, sync::Arc, - time::Duration, usize, }; use tasks_ui::{TaskOverrides, TasksModal}; @@ -13,45 +12,47 @@ use tasks_ui::{TaskOverrides, TasksModal}; use dap::{ DapRegistry, DebugRequest, TelemetrySpawnLocation, adapters::DebugAdapterName, send_telemetry, }; -use editor::{Anchor, Editor, EditorElement, EditorStyle, scroll::Autoscroll}; +use editor::{Editor, EditorElement, EditorStyle}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - Animation, AnimationExt as _, App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, - Focusable, KeyContext, Render, Subscription, TextStyle, Transformation, WeakEntity, percentage, + App, AppContext, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle, + InteractiveText, KeyContext, PromptButton, PromptLevel, Render, StyledText, Subscription, + TextStyle, UnderlineStyle, WeakEntity, }; use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMatch}; use project::{ProjectPath, TaskContexts, TaskSourceKind, task_store::TaskStore}; -use settings::Settings; -use task::{DebugScenario, LaunchRequest, RevealTarget, ZedDebugConfig}; +use settings::{Settings, initial_local_debug_tasks_content}; +use task::{DebugScenario, RevealTarget, ZedDebugConfig}; use theme::ThemeSettings; use ui::{ ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context, - ContextMenu, Disableable, DropdownMenu, FluentBuilder, Icon, IconButton, IconName, IconSize, + ContextMenu, Disableable, DropdownMenu, FluentBuilder, Icon, IconName, IconSize, IconWithIndicator, Indicator, InteractiveElement, IntoElement, Label, LabelCommon as _, ListItem, ListItemSpacing, ParentElement, RenderOnce, SharedString, Styled, StyledExt, - ToggleButton, ToggleState, Toggleable, Window, div, h_flex, relative, rems, v_flex, + StyledTypography, ToggleButton, ToggleState, Toggleable, Window, div, h_flex, px, relative, + rems, v_flex, }; use util::ResultExt; use workspace::{ModalView, Workspace, pane}; use crate::{attach_modal::AttachModal, debugger_panel::DebugPanel}; -enum SaveScenarioState { - Saving, - Saved((ProjectPath, SharedString)), - Failed(SharedString), -} +// enum SaveScenarioState { +// Saving, +// Saved((ProjectPath, SharedString)), +// Failed(SharedString), +// } -pub(super) struct NewSessionModal { +pub(super) struct NewProcessModal { workspace: WeakEntity, debug_panel: WeakEntity, - mode: NewSessionMode, - launch_picker: Entity>, + mode: NewProcessMode, + debug_picker: Entity>, attach_mode: Entity, - configure_mode: Entity, + launch_mode: Entity, task_mode: TaskMode, debugger: Option, - save_scenario_state: Option, + // save_scenario_state: Option, _subscriptions: [Subscription; 3], } @@ -73,11 +74,11 @@ fn suggested_label(request: &DebugRequest, debugger: &str) -> SharedString { } } -impl NewSessionModal { +impl NewProcessModal { pub(super) fn show( workspace: &mut Workspace, window: &mut Window, - mode: NewSessionMode, + mode: NewProcessMode, reveal_target: Option, cx: &mut Context, ) { @@ -101,12 +102,12 @@ impl NewSessionModal { let launch_picker = cx.new(|cx| { let mut delegate = - DebugScenarioDelegate::new(debug_panel.downgrade(), task_store.clone()); + DebugDelegate::new(debug_panel.downgrade(), task_store.clone()); delegate.task_contexts_loaded(task_contexts.clone(), languages, window, cx); Picker::uniform_list(delegate, window, cx).modal(false) }); - let configure_mode = ConfigureMode::new(None, window, cx); + let configure_mode = LaunchMode::new(window, cx); if let Some(active_cwd) = task_contexts .active_context() .and_then(|context| context.cwd.clone()) @@ -148,15 +149,15 @@ impl NewSessionModal { ]; Self { - launch_picker, + debug_picker: launch_picker, attach_mode, - configure_mode, + launch_mode: configure_mode, task_mode, debugger: None, mode, debug_panel: debug_panel.downgrade(), workspace: workspace_handle, - save_scenario_state: None, + // save_scenario_state: None, _subscriptions, } }); @@ -170,49 +171,49 @@ impl NewSessionModal { fn render_mode(&mut self, window: &mut Window, cx: &mut Context) -> impl ui::IntoElement { let dap_menu = self.adapter_drop_down_menu(window, cx); match self.mode { - NewSessionMode::Task => self + NewProcessMode::Task => self .task_mode .task_modal .read(cx) .picker .clone() .into_any_element(), - NewSessionMode::Attach => self.attach_mode.update(cx, |this, cx| { + NewProcessMode::Attach => self.attach_mode.update(cx, |this, cx| { this.clone().render(window, cx).into_any_element() }), - NewSessionMode::Configure => self.configure_mode.update(cx, |this, cx| { + NewProcessMode::Launch => self.launch_mode.update(cx, |this, cx| { this.clone().render(dap_menu, window, cx).into_any_element() }), - NewSessionMode::Launch => v_flex() + NewProcessMode::Debug => v_flex() .w(rems(34.)) - .child(self.launch_picker.clone()) + .child(self.debug_picker.clone()) .into_any_element(), } } fn mode_focus_handle(&self, cx: &App) -> FocusHandle { match self.mode { - NewSessionMode::Task => self.task_mode.task_modal.focus_handle(cx), - NewSessionMode::Attach => self.attach_mode.read(cx).attach_picker.focus_handle(cx), - NewSessionMode::Configure => self.configure_mode.read(cx).program.focus_handle(cx), - NewSessionMode::Launch => self.launch_picker.focus_handle(cx), + NewProcessMode::Task => self.task_mode.task_modal.focus_handle(cx), + NewProcessMode::Attach => self.attach_mode.read(cx).attach_picker.focus_handle(cx), + NewProcessMode::Launch => self.launch_mode.read(cx).program.focus_handle(cx), + NewProcessMode::Debug => self.debug_picker.focus_handle(cx), } } fn debug_scenario(&self, debugger: &str, cx: &App) -> Option { let request = match self.mode { - NewSessionMode::Configure => Some(DebugRequest::Launch( - self.configure_mode.read(cx).debug_request(cx), + NewProcessMode::Launch => Some(DebugRequest::Launch( + self.launch_mode.read(cx).debug_request(cx), )), - NewSessionMode::Attach => Some(DebugRequest::Attach( + NewProcessMode::Attach => Some(DebugRequest::Attach( self.attach_mode.read(cx).debug_request(), )), _ => None, }?; let label = suggested_label(&request, debugger); - let stop_on_entry = if let NewSessionMode::Configure = &self.mode { - Some(self.configure_mode.read(cx).stop_on_entry.selected()) + let stop_on_entry = if let NewProcessMode::Launch = &self.mode { + Some(self.launch_mode.read(cx).stop_on_entry.selected()) } else { None }; @@ -229,18 +230,29 @@ impl NewSessionModal { .and_then(|adapter| adapter.config_from_zed_format(session_scenario).ok()) } - fn start_new_session(&self, window: &mut Window, cx: &mut Context) { - let Some(debugger) = self.debugger.as_ref() else { + fn start_new_session(&mut self, window: &mut Window, cx: &mut Context) { + if self.debugger.as_ref().is_none() { return; - }; + } - if let NewSessionMode::Launch = &self.mode { - self.launch_picker.update(cx, |picker, cx| { + if let NewProcessMode::Debug = &self.mode { + self.debug_picker.update(cx, |picker, cx| { picker.delegate.confirm(false, window, cx); }); return; } + // TODO: Restore once we have proper, comment preserving edits + // if let NewProcessMode::Launch = &self.mode { + // if self.launch_mode.read(cx).save_to_debug_json.selected() { + // self.save_debug_scenario(window, cx); + // } + // } + + let Some(debugger) = self.debugger.as_ref() else { + return; + }; + let Some(config) = self.debug_scenario(debugger, cx) else { log::error!("debug config not found in mode: {}", self.mode); return; @@ -289,179 +301,50 @@ impl NewSessionModal { } fn task_contexts(&self, cx: &App) -> Option> { - self.launch_picker.read(cx).delegate.task_contexts.clone() + self.debug_picker.read(cx).delegate.task_contexts.clone() } - fn save_debug_scenario(&mut self, window: &mut Window, cx: &mut Context) { - let Some((save_scenario, scenario_label)) = self - .debugger - .as_ref() - .and_then(|debugger| self.debug_scenario(&debugger, cx)) - .zip(self.task_contexts(cx).and_then(|tcx| tcx.worktree())) - .and_then(|(scenario, worktree_id)| { - self.debug_panel - .update(cx, |panel, cx| { - panel.save_scenario(&scenario, worktree_id, window, cx) - }) - .ok() - .zip(Some(scenario.label.clone())) - }) - else { - return; - }; + // fn save_debug_scenario(&mut self, window: &mut Window, cx: &mut Context) { + // let Some((save_scenario, scenario_label)) = self + // .debugger + // .as_ref() + // .and_then(|debugger| self.debug_scenario(&debugger, cx)) + // .zip(self.task_contexts(cx).and_then(|tcx| tcx.worktree())) + // .and_then(|(scenario, worktree_id)| { + // self.debug_panel + // .update(cx, |panel, cx| { + // panel.save_scenario(&scenario, worktree_id, window, cx) + // }) + // .ok() + // .zip(Some(scenario.label.clone())) + // }) + // else { + // return; + // }; - self.save_scenario_state = Some(SaveScenarioState::Saving); + // self.save_scenario_state = Some(SaveScenarioState::Saving); - cx.spawn(async move |this, cx| { - let res = save_scenario.await; + // cx.spawn(async move |this, cx| { + // let res = save_scenario.await; - this.update(cx, |this, _| match res { - Ok(saved_file) => { - this.save_scenario_state = - Some(SaveScenarioState::Saved((saved_file, scenario_label))) - } - Err(error) => { - this.save_scenario_state = - Some(SaveScenarioState::Failed(error.to_string().into())) - } - }) - .ok(); + // this.update(cx, |this, _| match res { + // Ok(saved_file) => { + // this.save_scenario_state = + // Some(SaveScenarioState::Saved((saved_file, scenario_label))) + // } + // Err(error) => { + // this.save_scenario_state = + // Some(SaveScenarioState::Failed(error.to_string().into())) + // } + // }) + // .ok(); - cx.background_executor().timer(Duration::from_secs(3)).await; - this.update(cx, |this, _| this.save_scenario_state.take()) - .ok(); - }) - .detach(); - } - - fn render_save_state(&self, cx: &mut Context) -> impl IntoElement { - let this_entity = cx.weak_entity().clone(); - - div().when_some(self.save_scenario_state.as_ref(), { - let this_entity = this_entity.clone(); - - move |this, save_state| match save_state { - SaveScenarioState::Saved((saved_path, scenario_label)) => this.child( - IconButton::new("new-session-modal-go-to-file", IconName::ArrowUpRight) - .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .on_click({ - let this_entity = this_entity.clone(); - let saved_path = saved_path.clone(); - let scenario_label = scenario_label.clone(); - move |_, window, cx| { - window - .spawn(cx, { - let this_entity = this_entity.clone(); - let saved_path = saved_path.clone(); - let scenario_label = scenario_label.clone(); - - async move |cx| { - let editor = this_entity - .update_in(cx, |this, window, cx| { - this.workspace.update(cx, |workspace, cx| { - workspace.open_path( - saved_path.clone(), - None, - true, - window, - cx, - ) - }) - })?? - .await?; - - cx.update(|window, cx| { - if let Some(editor) = editor.act_as::(cx) { - editor.update(cx, |editor, cx| { - let row = editor - .text(cx) - .lines() - .enumerate() - .find_map(|(row, text)| { - if text.contains( - scenario_label.as_ref(), - ) { - Some(row) - } else { - None - } - })?; - - let buffer = editor.buffer().read(cx); - let excerpt_id = - *buffer.excerpt_ids().first()?; - - let snapshot = buffer - .as_singleton()? - .read(cx) - .snapshot(); - - let anchor = snapshot.anchor_before( - Point::new(row as u32, 0), - ); - - let anchor = Anchor { - buffer_id: anchor.buffer_id, - excerpt_id, - text_anchor: anchor, - diff_base_anchor: None, - }; - - editor.change_selections( - Some(Autoscroll::center()), - window, - cx, - |selections| { - let id = - selections.new_selection_id(); - selections.select_anchors( - vec![Selection { - id, - start: anchor, - end: anchor, - reversed: false, - goal: language::SelectionGoal::None - }], - ); - }, - ); - - Some(()) - }); - } - })?; - - this_entity - .update(cx, |_, cx| cx.emit(DismissEvent)) - .ok(); - - anyhow::Ok(()) - } - }) - .detach(); - } - }), - ), - SaveScenarioState::Saving => this.child( - Icon::new(IconName::Spinner) - .size(IconSize::Small) - .color(Color::Muted) - .with_animation( - "Spinner", - Animation::new(Duration::from_secs(3)).repeat(), - |icon, delta| icon.transform(Transformation::rotate(percentage(delta))), - ), - ), - SaveScenarioState::Failed(error_msg) => this.child( - IconButton::new("Failed Scenario Saved", IconName::X) - .icon_size(IconSize::Small) - .icon_color(Color::Error) - .tooltip(ui::Tooltip::text(error_msg.clone())), - ), - } - }) - } + // cx.background_executor().timer(Duration::from_secs(3)).await; + // this.update(cx, |this, _| this.save_scenario_state.take()) + // .ok(); + // }) + // .detach(); + // } fn adapter_drop_down_menu( &mut self, @@ -513,7 +396,7 @@ impl NewSessionModal { weak.update(cx, |this, cx| { this.debugger = Some(name.clone()); cx.notify(); - if let NewSessionMode::Attach = &this.mode { + if let NewProcessMode::Attach = &this.mode { Self::update_attach_picker(&this.attach_mode, &name, window, cx); } }) @@ -529,32 +412,96 @@ impl NewSessionModal { }), ) } + + fn open_debug_json(&self, window: &mut Window, cx: &mut Context) { + let this = cx.entity(); + window + .spawn(cx, async move |cx| { + let worktree_id = this.update(cx, |this, cx| { + let tcx = this.task_contexts(cx); + tcx?.worktree() + })?; + + let Some(worktree_id) = worktree_id else { + let _ = cx.prompt( + PromptLevel::Critical, + "Cannot open debug.json", + Some("You must have at least one project open"), + &[PromptButton::ok("Ok")], + ); + return Ok(()); + }; + + let editor = this + .update_in(cx, |this, window, cx| { + this.workspace.update(cx, |workspace, cx| { + workspace.open_path( + ProjectPath { + worktree_id, + path: local_debug_file_relative_path().into(), + }, + None, + true, + window, + cx, + ) + }) + })?? + .await?; + + cx.update(|_window, cx| { + if let Some(editor) = editor.act_as::(cx) { + editor.update(cx, |editor, cx| { + editor.buffer().update(cx, |buffer, cx| { + if let Some(singleton) = buffer.as_singleton() { + singleton.update(cx, |buffer, cx| { + if buffer.is_empty() { + buffer.edit( + [(0..0, initial_local_debug_tasks_content())], + None, + cx, + ); + } + }) + } + }) + }); + } + }) + .ok(); + + this.update(cx, |_, cx| cx.emit(DismissEvent)).ok(); + + anyhow::Ok(()) + }) + .detach(); + } } static SELECT_DEBUGGER_LABEL: SharedString = SharedString::new_static("Select Debugger"); #[derive(Clone)] -pub(crate) enum NewSessionMode { +pub(crate) enum NewProcessMode { Task, - Configure, - Attach, Launch, + Attach, + Debug, } -impl std::fmt::Display for NewSessionMode { +impl std::fmt::Display for NewProcessMode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mode = match self { - NewSessionMode::Task => "Run", - NewSessionMode::Launch => "Debug", - NewSessionMode::Attach => "Attach", - NewSessionMode::Configure => "Configure Debugger", + NewProcessMode::Task => "Run", + NewProcessMode::Debug => "Debug", + NewProcessMode::Attach => "Attach", + NewProcessMode::Launch => "Launch", }; write!(f, "{}", mode) } } -impl Focusable for NewSessionMode { +impl Focusable for NewProcessMode { fn focus_handle(&self, cx: &App) -> FocusHandle { cx.focus_handle() } @@ -598,7 +545,7 @@ fn render_editor(editor: &Entity, window: &mut Window, cx: &App) -> impl .bg(theme.colors().editor_background) } -impl Render for NewSessionModal { +impl Render for NewProcessModal { fn render( &mut self, window: &mut ui::Window, @@ -620,10 +567,10 @@ impl Render for NewSessionModal { })) .on_action(cx.listener(|this, _: &pane::ActivateNextItem, window, cx| { this.mode = match this.mode { - NewSessionMode::Task => NewSessionMode::Launch, - NewSessionMode::Launch => NewSessionMode::Attach, - NewSessionMode::Attach => NewSessionMode::Configure, - NewSessionMode::Configure => NewSessionMode::Task, + NewProcessMode::Task => NewProcessMode::Debug, + NewProcessMode::Debug => NewProcessMode::Attach, + NewProcessMode::Attach => NewProcessMode::Launch, + NewProcessMode::Launch => NewProcessMode::Task, }; this.mode_focus_handle(cx).focus(window); @@ -631,10 +578,10 @@ impl Render for NewSessionModal { .on_action( cx.listener(|this, _: &pane::ActivatePreviousItem, window, cx| { this.mode = match this.mode { - NewSessionMode::Task => NewSessionMode::Configure, - NewSessionMode::Launch => NewSessionMode::Task, - NewSessionMode::Attach => NewSessionMode::Launch, - NewSessionMode::Configure => NewSessionMode::Attach, + NewProcessMode::Task => NewProcessMode::Launch, + NewProcessMode::Debug => NewProcessMode::Task, + NewProcessMode::Attach => NewProcessMode::Debug, + NewProcessMode::Launch => NewProcessMode::Attach, }; this.mode_focus_handle(cx).focus(window); @@ -652,13 +599,13 @@ impl Render for NewSessionModal { .child( ToggleButton::new( "debugger-session-ui-tasks-button", - NewSessionMode::Task.to_string(), + NewProcessMode::Task.to_string(), ) .size(ButtonSize::Default) - .toggle_state(matches!(self.mode, NewSessionMode::Task)) + .toggle_state(matches!(self.mode, NewProcessMode::Task)) .style(ui::ButtonStyle::Subtle) .on_click(cx.listener(|this, _, window, cx| { - this.mode = NewSessionMode::Task; + this.mode = NewProcessMode::Task; this.mode_focus_handle(cx).focus(window); cx.notify(); })) @@ -667,13 +614,13 @@ impl Render for NewSessionModal { .child( ToggleButton::new( "debugger-session-ui-launch-button", - NewSessionMode::Launch.to_string(), + NewProcessMode::Debug.to_string(), ) .size(ButtonSize::Default) .style(ui::ButtonStyle::Subtle) - .toggle_state(matches!(self.mode, NewSessionMode::Launch)) + .toggle_state(matches!(self.mode, NewProcessMode::Debug)) .on_click(cx.listener(|this, _, window, cx| { - this.mode = NewSessionMode::Launch; + this.mode = NewProcessMode::Debug; this.mode_focus_handle(cx).focus(window); cx.notify(); })) @@ -682,13 +629,13 @@ impl Render for NewSessionModal { .child( ToggleButton::new( "debugger-session-ui-attach-button", - NewSessionMode::Attach.to_string(), + NewProcessMode::Attach.to_string(), ) .size(ButtonSize::Default) - .toggle_state(matches!(self.mode, NewSessionMode::Attach)) + .toggle_state(matches!(self.mode, NewProcessMode::Attach)) .style(ui::ButtonStyle::Subtle) .on_click(cx.listener(|this, _, window, cx| { - this.mode = NewSessionMode::Attach; + this.mode = NewProcessMode::Attach; if let Some(debugger) = this.debugger.as_ref() { Self::update_attach_picker( @@ -706,13 +653,13 @@ impl Render for NewSessionModal { .child( ToggleButton::new( "debugger-session-ui-custom-button", - NewSessionMode::Configure.to_string(), + NewProcessMode::Launch.to_string(), ) .size(ButtonSize::Default) - .toggle_state(matches!(self.mode, NewSessionMode::Configure)) + .toggle_state(matches!(self.mode, NewProcessMode::Launch)) .style(ui::ButtonStyle::Subtle) .on_click(cx.listener(|this, _, window, cx| { - this.mode = NewSessionMode::Configure; + this.mode = NewProcessMode::Launch; this.mode_focus_handle(cx).focus(window); cx.notify(); })) @@ -733,30 +680,42 @@ impl Render for NewSessionModal { .border_t_1() .w_full(); match self.mode { - NewSessionMode::Configure => el.child( + NewProcessMode::Launch => el.child( container .child( h_flex() + .text_ui_sm(cx) + .text_color(Color::Muted.color(cx)) .child( - Button::new( - "new-session-modal-back", - "Save to .zed/debug.json...", + InteractiveText::new( + "open-debug-json", + StyledText::new( + "Open .zed/debug.json for advanced configuration", + ) + .with_highlights([( + 5..20, + HighlightStyle { + underline: Some(UnderlineStyle { + thickness: px(1.0), + color: None, + wavy: false, + }), + ..Default::default() + }, + )]), ) - .on_click(cx.listener(|this, _, window, cx| { - this.save_debug_scenario(window, cx); - })) - .disabled( - self.debugger.is_none() - || self - .configure_mode - .read(cx) - .program - .read(cx) - .is_empty(cx) - || self.save_scenario_state.is_some(), + .on_click( + vec![5..20], + { + let this = cx.entity(); + move |_, window, cx| { + this.update(cx, |this, cx| { + this.open_debug_json(window, cx); + }) + } + }, ), - ) - .child(self.render_save_state(cx)), + ), ) .child( Button::new("debugger-spawn", "Start") @@ -766,7 +725,7 @@ impl Render for NewSessionModal { .disabled( self.debugger.is_none() || self - .configure_mode + .launch_mode .read(cx) .program .read(cx) @@ -774,7 +733,7 @@ impl Render for NewSessionModal { ), ), ), - NewSessionMode::Attach => el.child( + NewProcessMode::Attach => el.child( container .child(div().child(self.adapter_drop_down_menu(window, cx))) .child( @@ -797,21 +756,21 @@ impl Render for NewSessionModal { ), ), ), - NewSessionMode::Launch => el, - NewSessionMode::Task => el, + NewProcessMode::Debug => el, + NewProcessMode::Task => el, } }) } } -impl EventEmitter for NewSessionModal {} -impl Focusable for NewSessionModal { +impl EventEmitter for NewProcessModal {} +impl Focusable for NewProcessModal { fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle { self.mode_focus_handle(cx) } } -impl ModalView for NewSessionModal {} +impl ModalView for NewProcessModal {} impl RenderOnce for AttachMode { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { @@ -823,44 +782,30 @@ impl RenderOnce for AttachMode { } #[derive(Clone)] -pub(super) struct ConfigureMode { +pub(super) struct LaunchMode { program: Entity, cwd: Entity, stop_on_entry: ToggleState, + // save_to_debug_json: ToggleState, } -impl ConfigureMode { - pub(super) fn new( - past_launch_config: Option, - window: &mut Window, - cx: &mut App, - ) -> Entity { - let (past_program, past_cwd) = past_launch_config - .map(|config| (Some(config.program), config.cwd)) - .unwrap_or_else(|| (None, None)); - +impl LaunchMode { + pub(super) fn new(window: &mut Window, cx: &mut App) -> Entity { let program = cx.new(|cx| Editor::single_line(window, cx)); program.update(cx, |this, cx| { - this.set_placeholder_text( - "ALPHA=\"Windows\" BETA=\"Wen\" your_program --arg1 --arg2=arg3", - cx, - ); - - if let Some(past_program) = past_program { - this.set_text(past_program, window, cx); - }; + this.set_placeholder_text("ENV=Zed ~/bin/debugger --launch", 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); - }; + this.set_placeholder_text("Ex: $ZED_WORKTREE_ROOT", cx); }); + cx.new(|_| Self { program, cwd, stop_on_entry: ToggleState::Unselected, + // save_to_debug_json: ToggleState::Unselected, }) } @@ -873,11 +818,17 @@ impl ConfigureMode { } pub(super) fn debug_request(&self, cx: &App) -> task::LaunchRequest { - let path = self.cwd.read(cx).text(cx); + let cwd_text = self.cwd.read(cx).text(cx); + let cwd = if cwd_text.is_empty() { + None + } else { + Some(PathBuf::from(cwd_text)) + }; + if cfg!(windows) { return task::LaunchRequest { program: self.program.read(cx).text(cx), - cwd: path.is_empty().not().then(|| PathBuf::from(path)), + cwd, args: Default::default(), env: Default::default(), }; @@ -902,7 +853,7 @@ impl ConfigureMode { task::LaunchRequest { program, - cwd: path.is_empty().not().then(|| PathBuf::from(path)), + cwd, args, env, } @@ -929,7 +880,17 @@ impl ConfigureMode { .gap(ui::DynamicSpacing::Base08.rems(cx)) .child(adapter_menu), ) + .child( + Label::new("Debugger Program") + .size(ui::LabelSize::Small) + .color(Color::Muted), + ) .child(render_editor(&self.program, window, cx)) + .child( + Label::new("Working Directory") + .size(ui::LabelSize::Small) + .color(Color::Muted), + ) .child(render_editor(&self.cwd, window, cx)) .child( CheckboxWithLabel::new( @@ -950,6 +911,27 @@ impl ConfigureMode { ) .checkbox_position(ui::IconPosition::End), ) + // TODO: restore once we have proper, comment preserving + // file edits. + // .child( + // CheckboxWithLabel::new( + // "debugger-save-to-debug-json", + // Label::new("Save to debug.json") + // .size(ui::LabelSize::Small) + // .color(Color::Muted), + // self.save_to_debug_json, + // { + // let this = cx.weak_entity(); + // move |state, _, cx| { + // this.update(cx, |this, _| { + // this.save_to_debug_json = *state; + // }) + // .ok(); + // } + // }, + // ) + // .checkbox_position(ui::IconPosition::End), + // ) } } @@ -964,7 +946,7 @@ impl AttachMode { debugger: Option, workspace: WeakEntity, window: &mut Window, - cx: &mut Context, + cx: &mut Context, ) -> Entity { let definition = ZedDebugConfig { adapter: debugger.unwrap_or(DebugAdapterName("".into())).0, @@ -994,7 +976,7 @@ pub(super) struct TaskMode { pub(super) task_modal: Entity, } -pub(super) struct DebugScenarioDelegate { +pub(super) struct DebugDelegate { task_store: Entity, candidates: Vec<(Option, DebugScenario)>, selected_index: usize, @@ -1006,7 +988,7 @@ pub(super) struct DebugScenarioDelegate { last_used_candidate_index: Option, } -impl DebugScenarioDelegate { +impl DebugDelegate { pub(super) fn new(debug_panel: WeakEntity, task_store: Entity) -> Self { Self { task_store, @@ -1085,7 +1067,7 @@ impl DebugScenarioDelegate { } } -impl PickerDelegate for DebugScenarioDelegate { +impl PickerDelegate for DebugDelegate { type ListItem = ui::ListItem; fn match_count(&self) -> usize { @@ -1270,37 +1252,38 @@ pub(crate) fn resolve_path(path: &mut String) { } #[cfg(test)] -impl NewSessionModal { - pub(crate) fn set_configure( - &mut self, - program: impl AsRef, - cwd: impl AsRef, - stop_on_entry: bool, - window: &mut Window, - cx: &mut Context, - ) { - self.mode = NewSessionMode::Configure; - self.debugger = Some(dap::adapters::DebugAdapterName("fake-adapter".into())); +impl NewProcessModal { + // #[cfg(test)] + // pub(crate) fn set_configure( + // &mut self, + // program: impl AsRef, + // cwd: impl AsRef, + // stop_on_entry: bool, + // window: &mut Window, + // cx: &mut Context, + // ) { + // self.mode = NewProcessMode::Launch; + // self.debugger = Some(dap::adapters::DebugAdapterName("fake-adapter".into())); - self.configure_mode.update(cx, |configure, cx| { - configure.program.update(cx, |editor, cx| { - editor.clear(window, cx); - editor.set_text(program.as_ref(), window, cx); - }); + // self.launch_mode.update(cx, |configure, cx| { + // configure.program.update(cx, |editor, cx| { + // editor.clear(window, cx); + // editor.set_text(program.as_ref(), window, cx); + // }); - configure.cwd.update(cx, |editor, cx| { - editor.clear(window, cx); - editor.set_text(cwd.as_ref(), window, cx); - }); + // configure.cwd.update(cx, |editor, cx| { + // editor.clear(window, cx); + // editor.set_text(cwd.as_ref(), window, cx); + // }); - configure.stop_on_entry = match stop_on_entry { - true => ToggleState::Selected, - _ => ToggleState::Unselected, - } - }) - } + // configure.stop_on_entry = match stop_on_entry { + // true => ToggleState::Selected, + // _ => ToggleState::Unselected, + // } + // }) + // } - pub(crate) fn save_scenario(&mut self, window: &mut Window, cx: &mut Context) { - self.save_debug_scenario(window, cx); - } + // pub(crate) fn save_scenario(&mut self, _window: &mut Window, _cx: &mut Context) { + // self.save_debug_scenario(window, cx); + // } } diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index a7b058e332..5b85b51faa 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -8,7 +8,7 @@ pub mod variable_list; use std::{any::Any, ops::ControlFlow, path::PathBuf, sync::Arc, time::Duration}; use crate::{ - new_session_modal::resolve_path, + new_process_modal::resolve_path, persistence::{self, DebuggerPaneItem, SerializedLayout}, }; @@ -566,7 +566,7 @@ impl RunningState { } } - pub(crate) fn relativlize_paths( + pub(crate) fn relativize_paths( key: Option<&str>, config: &mut serde_json::Value, context: &TaskContext, @@ -574,12 +574,12 @@ impl RunningState { match config { serde_json::Value::Object(obj) => { obj.iter_mut() - .for_each(|(key, value)| Self::relativlize_paths(Some(key), value, context)); + .for_each(|(key, value)| Self::relativize_paths(Some(key), value, context)); } serde_json::Value::Array(array) => { array .iter_mut() - .for_each(|value| Self::relativlize_paths(None, value, context)); + .for_each(|value| Self::relativize_paths(None, value, context)); } serde_json::Value::String(s) if key == Some("program") || key == Some("cwd") => { // Some built-in zed tasks wrap their arguments in quotes as they might contain spaces. @@ -806,7 +806,7 @@ impl RunningState { mut config, tcp_connection, } = scenario; - Self::relativlize_paths(None, &mut config, &task_context); + Self::relativize_paths(None, &mut config, &task_context); Self::substitute_variables_in_config(&mut config, &task_context); let request_type = dap_registry diff --git a/crates/debugger_ui/src/tests.rs b/crates/debugger_ui/src/tests.rs index c04b97af55..0828f13714 100644 --- a/crates/debugger_ui/src/tests.rs +++ b/crates/debugger_ui/src/tests.rs @@ -25,7 +25,7 @@ mod inline_values; #[cfg(test)] mod module_list; #[cfg(test)] -mod new_session_modal; +mod new_process_modal; #[cfg(test)] mod persistence; #[cfg(test)] diff --git a/crates/debugger_ui/src/tests/new_session_modal.rs b/crates/debugger_ui/src/tests/new_process_modal.rs similarity index 69% rename from crates/debugger_ui/src/tests/new_session_modal.rs rename to crates/debugger_ui/src/tests/new_process_modal.rs index ad9f6f63da..6a2205acef 100644 --- a/crates/debugger_ui/src/tests/new_session_modal.rs +++ b/crates/debugger_ui/src/tests/new_process_modal.rs @@ -1,13 +1,13 @@ use dap::DapRegistry; use gpui::{BackgroundExecutor, TestAppContext, VisualTestContext}; -use project::{FakeFs, Fs, Project}; +use project::{FakeFs, Project}; use serde_json::json; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use task::{DebugRequest, DebugScenario, LaunchRequest, TaskContext, VariableName, ZedDebugConfig}; use util::path; -use crate::new_session_modal::NewSessionMode; +// use crate::new_process_modal::NewProcessMode; use crate::tests::{init_test, init_test_workspace}; #[gpui::test] @@ -152,111 +152,111 @@ async fn test_debug_session_substitutes_variables_and_relativizes_paths( } } -#[gpui::test] -async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut TestAppContext) { - init_test(cx); +// #[gpui::test] +// async fn test_save_debug_scenario_to_file(executor: BackgroundExecutor, cx: &mut TestAppContext) { +// init_test(cx); - let fs = FakeFs::new(executor.clone()); - fs.insert_tree( - path!("/project"), - json!({ - "main.rs": "fn main() {}" - }), - ) - .await; +// let fs = FakeFs::new(executor.clone()); +// fs.insert_tree( +// path!("/project"), +// json!({ +// "main.rs": "fn main() {}" +// }), +// ) +// .await; - let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await; - let workspace = init_test_workspace(&project, cx).await; - let cx = &mut VisualTestContext::from_window(*workspace, cx); +// let project = Project::test(fs.clone(), [path!("/project").as_ref()], cx).await; +// let workspace = init_test_workspace(&project, cx).await; +// let cx = &mut VisualTestContext::from_window(*workspace, cx); - workspace - .update(cx, |workspace, window, cx| { - crate::new_session_modal::NewSessionModal::show( - workspace, - window, - NewSessionMode::Launch, - None, - cx, - ); - }) - .unwrap(); +// workspace +// .update(cx, |workspace, window, cx| { +// crate::new_process_modal::NewProcessModal::show( +// workspace, +// window, +// NewProcessMode::Debug, +// None, +// cx, +// ); +// }) +// .unwrap(); - cx.run_until_parked(); +// cx.run_until_parked(); - let modal = workspace - .update(cx, |workspace, _, cx| { - workspace.active_modal::(cx) - }) - .unwrap() - .expect("Modal should be active"); +// let modal = workspace +// .update(cx, |workspace, _, cx| { +// workspace.active_modal::(cx) +// }) +// .unwrap() +// .expect("Modal should be active"); - modal.update_in(cx, |modal, window, cx| { - modal.set_configure("/project/main", "/project", false, window, cx); - modal.save_scenario(window, cx); - }); +// modal.update_in(cx, |modal, window, cx| { +// modal.set_configure("/project/main", "/project", false, window, cx); +// modal.save_scenario(window, cx); +// }); - cx.executor().run_until_parked(); +// cx.executor().run_until_parked(); - let debug_json_content = fs - .load(path!("/project/.zed/debug.json").as_ref()) - .await - .expect("debug.json should exist"); +// let debug_json_content = fs +// .load(path!("/project/.zed/debug.json").as_ref()) +// .await +// .expect("debug.json should exist"); - let expected_content = vec![ - "[", - " {", - r#" "adapter": "fake-adapter","#, - r#" "label": "main (fake-adapter)","#, - r#" "request": "launch","#, - r#" "program": "/project/main","#, - r#" "cwd": "/project","#, - r#" "args": [],"#, - r#" "env": {}"#, - " }", - "]", - ]; +// let expected_content = vec![ +// "[", +// " {", +// r#" "adapter": "fake-adapter","#, +// r#" "label": "main (fake-adapter)","#, +// r#" "request": "launch","#, +// r#" "program": "/project/main","#, +// r#" "cwd": "/project","#, +// r#" "args": [],"#, +// r#" "env": {}"#, +// " }", +// "]", +// ]; - let actual_lines: Vec<&str> = debug_json_content.lines().collect(); - pretty_assertions::assert_eq!(expected_content, actual_lines); +// let actual_lines: Vec<&str> = debug_json_content.lines().collect(); +// pretty_assertions::assert_eq!(expected_content, actual_lines); - modal.update_in(cx, |modal, window, cx| { - modal.set_configure("/project/other", "/project", true, window, cx); - modal.save_scenario(window, cx); - }); +// modal.update_in(cx, |modal, window, cx| { +// modal.set_configure("/project/other", "/project", true, window, cx); +// modal.save_scenario(window, cx); +// }); - cx.executor().run_until_parked(); +// cx.executor().run_until_parked(); - let debug_json_content = fs - .load(path!("/project/.zed/debug.json").as_ref()) - .await - .expect("debug.json should exist after second save"); +// let debug_json_content = fs +// .load(path!("/project/.zed/debug.json").as_ref()) +// .await +// .expect("debug.json should exist after second save"); - let expected_content = vec![ - "[", - " {", - r#" "adapter": "fake-adapter","#, - r#" "label": "main (fake-adapter)","#, - r#" "request": "launch","#, - r#" "program": "/project/main","#, - r#" "cwd": "/project","#, - r#" "args": [],"#, - r#" "env": {}"#, - " },", - " {", - r#" "adapter": "fake-adapter","#, - r#" "label": "other (fake-adapter)","#, - r#" "request": "launch","#, - r#" "program": "/project/other","#, - r#" "cwd": "/project","#, - r#" "args": [],"#, - r#" "env": {}"#, - " }", - "]", - ]; +// let expected_content = vec![ +// "[", +// " {", +// r#" "adapter": "fake-adapter","#, +// r#" "label": "main (fake-adapter)","#, +// r#" "request": "launch","#, +// r#" "program": "/project/main","#, +// r#" "cwd": "/project","#, +// r#" "args": [],"#, +// r#" "env": {}"#, +// " },", +// " {", +// r#" "adapter": "fake-adapter","#, +// r#" "label": "other (fake-adapter)","#, +// r#" "request": "launch","#, +// r#" "program": "/project/other","#, +// r#" "cwd": "/project","#, +// r#" "args": [],"#, +// r#" "env": {}"#, +// " }", +// "]", +// ]; - let actual_lines: Vec<&str> = debug_json_content.lines().collect(); - pretty_assertions::assert_eq!(expected_content, actual_lines); -} +// let actual_lines: Vec<&str> = debug_json_content.lines().collect(); +// pretty_assertions::assert_eq!(expected_content, actual_lines); +// } #[gpui::test] async fn test_dap_adapter_config_conversion_and_validation(cx: &mut TestAppContext) { diff --git a/crates/paths/src/paths.rs b/crates/paths/src/paths.rs index 4fe429da2e..088189f814 100644 --- a/crates/paths/src/paths.rs +++ b/crates/paths/src/paths.rs @@ -408,6 +408,7 @@ pub fn task_file_name() -> &'static str { } /// Returns the relative path to a `debug.json` file within a project. +/// .zed/debug.json pub fn local_debug_file_relative_path() -> &'static Path { Path::new(".zed/debug.json") } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 89411ff2ce..2ecb38b5c6 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -115,3 +115,7 @@ pub fn initial_tasks_content() -> Cow<'static, str> { pub fn initial_debug_tasks_content() -> Cow<'static, str> { asset_str::("settings/initial_debug_tasks.json") } + +pub fn initial_local_debug_tasks_content() -> Cow<'static, str> { + asset_str::("settings/initial_local_debug_tasks.json") +} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 42cb33cbf7..659ba06067 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -50,8 +50,8 @@ use rope::Rope; use search::project_search::ProjectSearchBar; use settings::{ DEFAULT_KEYMAP_PATH, InvalidSettingsError, KeymapFile, KeymapFileLoadResult, Settings, - SettingsStore, VIM_KEYMAP_PATH, initial_debug_tasks_content, initial_project_settings_content, - initial_tasks_content, update_settings_file, + SettingsStore, VIM_KEYMAP_PATH, initial_local_debug_tasks_content, + initial_project_settings_content, initial_tasks_content, update_settings_file, }; use std::path::PathBuf; use std::sync::atomic::{self, AtomicBool}; @@ -740,6 +740,14 @@ fn register_actions( cx, ); }) + .register_action(move |_: &mut Workspace, _: &OpenDebugTasks, window, cx| { + open_settings_file( + paths::debug_scenarios_file(), + || settings::initial_debug_tasks_content().as_ref().into(), + window, + cx, + ); + }) .register_action(open_project_settings_file) .register_action(open_project_tasks_file) .register_action(open_project_debug_tasks_file) @@ -1508,7 +1516,7 @@ fn open_project_debug_tasks_file( open_local_file( workspace, local_debug_file_relative_path(), - initial_debug_tasks_content(), + initial_local_debug_tasks_content(), window, cx, )