debugger: Add console indicator and resolve debug configs from NewSessionModal (#28489)
The debug console will now show an indicator when it's unopened and there's unread messages. `NewSessionModal` attempts to resolve debug configurations before using the config to start debugging. This allows users to use zed's task variables in the modal prompt. I had to invert tasks_ui dependency on debugger_ui so `NewSessionModal` could get the correct `TaskContexts` by calling tasks_ui functions. A consequence of this workspace has a new event `ShowAttachModal` that I'm not a big fan of. @osiewicz if you have time could you please take a look to see if there's a way around adding the event. I'm open to pair on it too. Release Notes: - N/A
This commit is contained in:
parent
66dd6726df
commit
cf65d9437a
12 changed files with 204 additions and 82 deletions
|
@ -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",
|
||||
|
|
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -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 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() =>
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue