diff --git a/.zed/debug.json b/.zed/debug.json index edc38f4e7c..9259c42f47 100644 --- a/.zed/debug.json +++ b/.zed/debug.json @@ -1,13 +1,13 @@ [ { - "label": "Debug Zed with LLDB", - "adapter": "LLDB", + "label": "Debug Zed (CodeLLDB)", + "adapter": "CodeLLDB", "program": "$ZED_WORKTREE_ROOT/target/debug/zed", "request": "launch", "cwd": "$ZED_WORKTREE_ROOT" }, { - "label": "Debug Zed with GDB", + "label": "Debug Zed (GDB)", "adapter": "GDB", "program": "$ZED_WORKTREE_ROOT/target/debug/zed", "request": "launch", diff --git a/Cargo.lock b/Cargo.lock index 182ca8dc1a..ab3b46fd32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4232,6 +4232,7 @@ dependencies = [ "settings", "sysinfo", "task", + "tasks_ui", "terminal_view", "theme", "ui", @@ -14240,9 +14241,7 @@ version = "0.1.0" dependencies = [ "anyhow", "collections", - "debugger_ui", "editor", - "feature_flags", "file_icons", "fuzzy", "gpui", diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index 100bacf87b..239c47d07d 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -45,6 +45,7 @@ serde_json.workspace = true settings.workspace = true sysinfo.workspace = true task.workspace = true +tasks_ui.workspace = true terminal_view.workspace = true theme.workspace = true ui.workspace = true diff --git a/crates/debugger_ui/src/debugger_panel.rs b/crates/debugger_ui/src/debugger_panel.rs index 0586ff24ac..d9327711f9 100644 --- a/crates/debugger_ui/src/debugger_panel.rs +++ b/crates/debugger_ui/src/debugger_panel.rs @@ -77,8 +77,45 @@ impl DebugPanel { let project = workspace.project().clone(); let dap_store = project.read(cx).dap_store(); - let _subscriptions = - vec![cx.subscribe_in(&dap_store, window, Self::handle_dap_store_event)]; + let weak = cx.weak_entity(); + + let modal_subscription = + cx.observe_new::(move |_, window, cx| { + let modal_entity = cx.entity(); + + weak.update(cx, |_: &mut DebugPanel, cx| { + let Some(window) = window else { + log::error!("Debug panel couldn't subscribe to tasks modal because there was no window"); + return; + }; + + cx.subscribe_in( + &modal_entity, + window, + |panel, _, event: &tasks_ui::ShowAttachModal, window, cx| { + panel.workspace.update(cx, |workspace, cx| { + let project = workspace.project().clone(); + workspace.toggle_modal(window, cx, |window, cx| { + crate::attach_modal::AttachModal::new( + project, + event.debug_config.clone(), + true, + window, + cx, + ) + }); + }).ok(); + }, + ) + .detach(); + }) + .ok(); + }); + + let _subscriptions = vec![ + cx.subscribe_in(&dap_store, window, Self::handle_dap_store_event), + modal_subscription, + ]; let debug_panel = Self { size: px(300.), diff --git a/crates/debugger_ui/src/debugger_ui.rs b/crates/debugger_ui/src/debugger_ui.rs index 560333a12f..7a89b275ea 100644 --- a/crates/debugger_ui/src/debugger_ui.rs +++ b/crates/debugger_ui/src/debugger_ui.rs @@ -156,7 +156,17 @@ pub fn init(cx: &mut App) { }); } }, - ); + ) + .register_action(|workspace: &mut Workspace, _: &Start, window, cx| { + tasks_ui::toggle_modal( + workspace, + None, + task::TaskModal::DebugModal, + window, + cx, + ) + .detach(); + }); }) }) .detach(); @@ -237,7 +247,7 @@ pub fn init(cx: &mut App) { state.session().update(cx, |session, cx| { session.evaluate(text, None, stack_id, None, cx); - }) + }); }); Some(()) }); diff --git a/crates/debugger_ui/src/new_session_modal.rs b/crates/debugger_ui/src/new_session_modal.rs index a4fca9409f..b97af68fd6 100644 --- a/crates/debugger_ui/src/new_session_modal.rs +++ b/crates/debugger_ui/src/new_session_modal.rs @@ -102,7 +102,8 @@ impl NewSessionModal { }, }) } - fn start_new_session(&self, cx: &mut Context) -> Result<()> { + + fn start_new_session(&self, window: &mut Window, cx: &mut Context) -> Result<()> { let workspace = self.workspace.clone(); let config = self .debug_config(cx) @@ -112,10 +113,41 @@ impl NewSessionModal { panel.past_debug_definition = Some(config.clone()); }); + let task_contexts = workspace + .update(cx, |workspace, cx| { + tasks_ui::task_contexts(workspace, window, cx) + }) + .ok(); + cx.spawn(async move |this, cx| { + let task_context = if let Some(task) = task_contexts { + task.await + .active_worktree_context + .map_or(task::TaskContext::default(), |context| context.1) + } else { + task::TaskContext::default() + }; let project = workspace.update(cx, |workspace, _| workspace.project().clone())?; - let task = - project.update(cx, |this, cx| this.start_debug_session(config.into(), cx))?; + + let task = project.update(cx, |this, cx| { + if let Some(debug_config) = + config + .clone() + .to_zed_format() + .ok() + .and_then(|task_template| { + task_template + .resolve_task("debug_task", &task_context) + .and_then(|resolved_task| { + resolved_task.resolved_debug_adapter_config() + }) + }) + { + this.start_debug_session(debug_config, cx) + } else { + this.start_debug_session(config.into(), cx) + } + })?; let spawn_result = task.await; if spawn_result.is_ok() { this.update(cx, |_, cx| { @@ -614,8 +646,8 @@ impl Render for NewSessionModal { }) .child( Button::new("debugger-spawn", "Start") - .on_click(cx.listener(|this, _, _, cx| { - this.start_new_session(cx).log_err(); + .on_click(cx.listener(|this, _, window, cx| { + this.start_new_session(window, cx).log_err(); })) .disabled(self.debugger.is_none()), ), diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index a994634cf7..57bd8f605d 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -26,8 +26,8 @@ use rpc::proto::ViewId; use settings::Settings; use stack_frame_list::StackFrameList; use ui::{ - App, Context, ContextMenu, DropdownMenu, InteractiveElement, IntoElement, ParentElement, - Render, SharedString, Styled, Window, div, h_flex, v_flex, + AnyElement, App, Context, ContextMenu, DropdownMenu, InteractiveElement, IntoElement, Label, + LabelCommon as _, ParentElement, Render, SharedString, Styled, Window, div, h_flex, v_flex, }; use util::ResultExt; use variable_list::VariableList; @@ -86,6 +86,7 @@ struct SubView { inner: AnyView, pane_focus_handle: FocusHandle, tab_name: SharedString, + show_indicator: Box bool>, } impl SubView { @@ -93,12 +94,14 @@ impl SubView { pane_focus_handle: FocusHandle, view: AnyView, tab_name: SharedString, + show_indicator: Option bool>>, cx: &mut App, ) -> Entity { cx.new(|_| Self { tab_name, inner: view, pane_focus_handle, + show_indicator: show_indicator.unwrap_or(Box::new(|_| false)), }) } } @@ -110,8 +113,27 @@ impl Focusable for SubView { impl EventEmitter<()> for SubView {} impl Item for SubView { type Event = (); - fn tab_content_text(&self, _window: &Window, _cx: &App) -> Option { - Some(self.tab_name.clone()) + + fn tab_content( + &self, + params: workspace::item::TabContentParams, + _: &Window, + cx: &App, + ) -> AnyElement { + let label = Label::new(self.tab_name.clone()) + .color(params.text_color()) + .into_any_element(); + + if !params.selected && self.show_indicator.as_ref()(cx) { + return h_flex() + .justify_between() + .child(ui::Indicator::dot()) + .gap_2() + .child(label) + .into_any_element(); + } + + label } } @@ -315,6 +337,7 @@ impl RunningState { this.focus_handle(cx), stack_frame_list.clone().into(), SharedString::new_static("Frames"), + None, cx, )), true, @@ -329,6 +352,7 @@ impl RunningState { breakpoints.focus_handle(cx), breakpoints.into(), SharedString::new_static("Breakpoints"), + None, cx, )), true, @@ -346,6 +370,7 @@ impl RunningState { variable_list.focus_handle(cx), variable_list.clone().into(), SharedString::new_static("Variables"), + None, cx, )), true, @@ -359,6 +384,7 @@ impl RunningState { this.focus_handle(cx), module_list.clone().into(), SharedString::new_static("Modules"), + None, cx, )), false, @@ -371,11 +397,17 @@ impl RunningState { }); let rightmost_pane = new_debugger_pane(workspace.clone(), project.clone(), window, cx); rightmost_pane.update(cx, |this, cx| { + let weak_console = console.downgrade(); this.add_item( Box::new(SubView::new( this.focus_handle(cx), console.clone().into(), SharedString::new_static("Console"), + Some(Box::new(move |cx| { + weak_console + .read_with(cx, |console, cx| console.show_indicator(cx)) + .unwrap_or_default() + })), cx, )), true, diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index ece97efde3..d799708827 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -105,6 +105,10 @@ impl Console { } } + pub(crate) fn show_indicator(&self, cx: &App) -> bool { + self.session.read(cx).has_new_output(self.last_token) + } + pub fn add_messages<'a>( &mut self, events: impl Iterator, diff --git a/crates/project/src/debugger/session.rs b/crates/project/src/debugger/session.rs index 54f3550d2e..ff861771ef 100644 --- a/crates/project/src/debugger/session.rs +++ b/crates/project/src/debugger/session.rs @@ -1152,6 +1152,10 @@ impl Session { } } + pub fn has_new_output(&self, last_update: OutputToken) -> bool { + self.output_token.0.checked_sub(last_update.0).unwrap_or(0) != 0 + } + pub fn output( &self, since: OutputToken, diff --git a/crates/tasks_ui/Cargo.toml b/crates/tasks_ui/Cargo.toml index 479f0588e9..77b1434718 100644 --- a/crates/tasks_ui/Cargo.toml +++ b/crates/tasks_ui/Cargo.toml @@ -14,11 +14,9 @@ path = "src/tasks_ui.rs" [dependencies] anyhow.workspace = true collections.workspace = true -debugger_ui.workspace = true editor.workspace = true file_icons.workspace = true fuzzy.workspace = true -feature_flags.workspace = true itertools.workspace = true gpui.workspace = true menu.workspace = true diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index fe48381127..f483ee8e27 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -128,9 +128,9 @@ impl TasksModalDelegate { } } -pub(crate) struct TasksModal { +pub struct TasksModal { picker: Entity>, - _subscription: Subscription, + _subscription: [Subscription; 2], } impl TasksModal { @@ -156,9 +156,16 @@ impl TasksModal { cx, ) }); - let _subscription = cx.subscribe(&picker, |_, _, _, cx| { - cx.emit(DismissEvent); - }); + let _subscription = [ + cx.subscribe(&picker, |_, _, _: &DismissEvent, cx| { + cx.emit(DismissEvent); + }), + cx.subscribe(&picker, |_, _, event: &ShowAttachModal, cx| { + cx.emit(ShowAttachModal { + debug_config: event.debug_config.clone(), + }); + }), + ]; Self { picker, _subscription, @@ -179,7 +186,13 @@ impl Render for TasksModal { } } +pub struct ShowAttachModal { + pub debug_config: DebugTaskDefinition, +} + impl EventEmitter for TasksModal {} +impl EventEmitter for TasksModal {} +impl EventEmitter for Picker {} impl Focusable for TasksModal { fn focus_handle(&self, cx: &gpui::App) -> gpui::FocusHandle { @@ -321,7 +334,7 @@ impl PickerDelegate for TasksModalDelegate { fn confirm( &mut self, omit_history_entry: bool, - window: &mut Window, + _: &mut Window, cx: &mut Context>, ) { let current_match_index = self.selected_index(); @@ -346,51 +359,52 @@ impl PickerDelegate for TasksModalDelegate { } } - self.workspace - .update(cx, |workspace, cx| { - match task.task_type() { - TaskType::Debug(config) if config.locator.is_none() => { - let Some(config): Option = task - .resolved_debug_adapter_config() - .and_then(|config| config.try_into().ok()) - else { - return; - }; - let project = workspace.project().clone(); + match task.task_type() { + TaskType::Debug(config) if config.locator.is_none() => { + let Some(config): Option = task + .resolved_debug_adapter_config() + .and_then(|config| config.try_into().ok()) + else { + return; + }; - match &config.request { - DebugRequestType::Attach(attach_config) - if attach_config.process_id.is_none() => - { - workspace.toggle_modal(window, cx, |window, cx| { - debugger_ui::attach_modal::AttachModal::new( - project, - config.clone(), - true, - window, - cx, - ) - }); - } - _ => { - project.update(cx, |project, cx| { + match &config.request { + DebugRequestType::Attach(attach_config) + if attach_config.process_id.is_none() => + { + cx.emit(ShowAttachModal { + debug_config: config.clone(), + }); + return; + } + _ => { + self.workspace + .update(cx, |workspace, cx| { + workspace.project().update(cx, |project, cx| { project .start_debug_session(config.into(), cx) .detach_and_log_err(cx); }); - } - } + }) + .ok(); } - _ => schedule_resolved_task( - workspace, - task_source_kind, - task, - omit_history_entry, - cx, - ), - }; - }) - .ok(); + } + } + _ => { + self.workspace + .update(cx, |workspace, cx| { + schedule_resolved_task( + workspace, + task_source_kind, + task, + omit_history_entry, + cx, + ); + }) + .ok(); + } + }; + cx.emit(DismissEvent); } diff --git a/crates/tasks_ui/src/tasks_ui.rs b/crates/tasks_ui/src/tasks_ui.rs index 3fa4bf107e..0a1fdfe8a9 100644 --- a/crates/tasks_ui/src/tasks_ui.rs +++ b/crates/tasks_ui/src/tasks_ui.rs @@ -1,11 +1,9 @@ use std::path::Path; use collections::HashMap; -use debugger_ui::Start; use editor::Editor; -use feature_flags::{Debugger, FeatureFlagViewExt}; use gpui::{App, AppContext as _, Context, Entity, Task, Window}; -use modal::{TaskOverrides, TasksModal}; +use modal::TaskOverrides; use project::{Location, TaskContexts, TaskSourceKind, Worktree}; use task::{ RevealTarget, TaskContext, TaskId, TaskModal, TaskTemplate, TaskVariables, VariableName, @@ -15,11 +13,11 @@ use workspace::{Workspace, tasks::schedule_resolved_task}; mod modal; -pub use modal::{Rerun, Spawn}; +pub use modal::{Rerun, ShowAttachModal, Spawn, TasksModal}; pub fn init(cx: &mut App) { cx.observe_new( - |workspace: &mut Workspace, window: Option<&mut Window>, cx: &mut Context| { + |workspace: &mut Workspace, _: Option<&mut Window>, _: &mut Context| { workspace .register_action(spawn_task_or_modal) .register_action(move |workspace, action: &modal::Rerun, window, cx| { @@ -89,17 +87,6 @@ pub fn init(cx: &mut App) { toggle_modal(workspace, None, TaskModal::ScriptModal, window, cx).detach(); }; }); - - let Some(window) = window else { - return; - }; - - cx.when_flag_enabled::(window, |workspace, _, _| { - workspace.register_action(|workspace: &mut Workspace, _: &Start, window, cx| { - crate::toggle_modal(workspace, None, task::TaskModal::DebugModal, window, cx) - .detach(); - }); - }); }, ) .detach(); @@ -277,7 +264,11 @@ where }) } -fn task_contexts(workspace: &Workspace, window: &mut Window, cx: &mut App) -> Task { +pub fn task_contexts( + workspace: &Workspace, + window: &mut Window, + cx: &mut App, +) -> Task { let active_item = workspace.active_item(cx); let active_worktree = active_item .as_ref()