Detailed changes
@@ -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",
@@ -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",
@@ -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
@@ -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::<tasks_ui::TasksModal>(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.),
@@ -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(())
});
@@ -102,7 +102,8 @@ impl NewSessionModal {
},
})
}
- fn start_new_session(&self, cx: &mut Context<Self>) -> Result<()> {
+
+ fn start_new_session(&self, window: &mut Window, cx: &mut Context<Self>) -> 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()),
),
@@ -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<dyn Fn(&App) -> bool>,
}
impl SubView {
@@ -93,12 +94,14 @@ impl SubView {
pane_focus_handle: FocusHandle,
view: AnyView,
tab_name: SharedString,
+ show_indicator: Option<Box<dyn Fn(&App) -> bool>>,
cx: &mut App,
) -> Entity<Self> {
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<SharedString> {
- 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,
@@ -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<Item = &'a OutputEvent>,
@@ -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,
@@ -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
@@ -128,9 +128,9 @@ impl TasksModalDelegate {
}
}
-pub(crate) struct TasksModal {
+pub struct TasksModal {
picker: Entity<Picker<TasksModalDelegate>>,
- _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<DismissEvent> for TasksModal {}
+impl EventEmitter<ShowAttachModal> for TasksModal {}
+impl EventEmitter<ShowAttachModal> for Picker<TasksModalDelegate> {}
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<picker::Picker<Self>>,
) {
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<DebugTaskDefinition> = task
- .resolved_debug_adapter_config()
- .and_then(|config| config.try_into().ok())
- else {
- return;
- };
- let project = workspace.project().clone();
-
- 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 task.task_type() {
+ TaskType::Debug(config) if config.locator.is_none() => {
+ let Some(config): Option<DebugTaskDefinition> = 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() =>
+ {
+ 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);
}
@@ -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>| {
+ |workspace: &mut Workspace, _: Option<&mut Window>, _: &mut Context<Workspace>| {
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::<Debugger>(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<TaskContexts> {
+pub fn task_contexts(
+ workspace: &Workspace,
+ window: &mut Window,
+ cx: &mut App,
+) -> Task<TaskContexts> {
let active_item = workspace.active_item(cx);
let active_worktree = active_item
.as_ref()