Cargo.lock 🔗
@@ -4248,6 +4248,7 @@ dependencies = [
"util",
"workspace",
"workspace-hack",
+ "zed_actions",
"zlog",
]
Cole Miller created
Release Notes:
- Debugger Beta: made it possible to navigate the breakpoint list using
menu keybindings.
Cargo.lock | 1
assets/keymaps/default-linux.json | 7
assets/keymaps/default-macos.json | 7
crates/debugger_ui/Cargo.toml | 1
crates/debugger_ui/src/session/running/breakpoint_list.rs | 538 ++++++--
crates/zed_actions/src/lib.rs | 2
6 files changed, 385 insertions(+), 171 deletions(-)
@@ -4248,6 +4248,7 @@ dependencies = [
"util",
"workspace",
"workspace-hack",
+ "zed_actions",
"zlog",
]
@@ -872,6 +872,13 @@
"ctrl-i": "debugger::ToggleSessionPicker"
}
},
+ {
+ "context": "BreakpointList",
+ "bindings": {
+ "space": "debugger::ToggleEnableBreakpoint",
+ "backspace": "debugger::UnsetBreakpoint"
+ }
+ },
{
"context": "CollabPanel && not_editing",
"bindings": {
@@ -932,6 +932,13 @@
"cmd-i": "debugger::ToggleSessionPicker"
}
},
+ {
+ "context": "BreakpointList",
+ "bindings": {
+ "space": "debugger::ToggleEnableBreakpoint",
+ "backspace": "debugger::UnsetBreakpoint"
+ }
+ },
{
"context": "CollabPanel && not_editing",
"use_key_equivalents": true,
@@ -63,6 +63,7 @@ workspace.workspace = true
workspace-hack.workspace = true
debugger_tools = { workspace = true, optional = true }
unindent = { workspace = true, optional = true }
+zed_actions.workspace = true
[dev-dependencies]
dap = { workspace = true, features = ["test-support"] }
@@ -1,13 +1,14 @@
use std::{
path::{Path, PathBuf},
+ sync::Arc,
time::Duration,
};
use dap::ExceptionBreakpointsFilter;
use editor::Editor;
use gpui::{
- AppContext, Entity, FocusHandle, Focusable, ListState, MouseButton, Stateful, Task, WeakEntity,
- list,
+ AppContext, Entity, FocusHandle, Focusable, MouseButton, ScrollStrategy, Stateful, Task,
+ UniformListScrollHandle, WeakEntity, uniform_list,
};
use language::Point;
use project::{
@@ -19,25 +20,27 @@ use project::{
worktree_store::WorktreeStore,
};
use ui::{
- App, Clickable, Color, Context, Div, Icon, IconButton, IconName, Indicator, InteractiveElement,
- IntoElement, Label, LabelCommon, LabelSize, ListItem, ParentElement, Render, RenderOnce,
- Scrollbar, ScrollbarState, SharedString, StatefulInteractiveElement, Styled, Tooltip, Window,
- div, h_flex, px, v_flex,
+ App, ButtonCommon, Clickable, Color, Context, Div, FluentBuilder as _, Icon, IconButton,
+ IconName, Indicator, InteractiveElement, IntoElement, Label, LabelCommon, LabelSize, ListItem,
+ ParentElement, Render, Scrollbar, ScrollbarState, SharedString, StatefulInteractiveElement,
+ Styled, Toggleable, Tooltip, Window, div, h_flex, px, v_flex,
};
-use util::{ResultExt, maybe};
+use util::ResultExt;
use workspace::Workspace;
+use zed_actions::{ToggleEnableBreakpoint, UnsetBreakpoint};
pub(crate) struct BreakpointList {
workspace: WeakEntity<Workspace>,
breakpoint_store: Entity<BreakpointStore>,
worktree_store: Entity<WorktreeStore>,
- list_state: ListState,
scrollbar_state: ScrollbarState,
breakpoints: Vec<BreakpointEntry>,
session: Entity<Session>,
hide_scrollbar_task: Option<Task<()>>,
show_scrollbar: bool,
focus_handle: FocusHandle,
+ scroll_handle: UniformListScrollHandle,
+ selected_ix: Option<usize>,
}
impl Focusable for BreakpointList {
@@ -56,36 +59,203 @@ impl BreakpointList {
let project = project.read(cx);
let breakpoint_store = project.breakpoint_store();
let worktree_store = project.worktree_store();
+ let focus_handle = cx.focus_handle();
+ let scroll_handle = UniformListScrollHandle::new();
+ let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
- cx.new(|cx| {
- let weak: gpui::WeakEntity<Self> = cx.weak_entity();
- let list_state = ListState::new(
- 0,
- gpui::ListAlignment::Top,
- px(1000.),
- move |ix, window, cx| {
- let Ok(Some(breakpoint)) =
- weak.update(cx, |this, _| this.breakpoints.get(ix).cloned())
- else {
- return div().into_any_element();
- };
-
- breakpoint.render(window, cx).into_any_element()
- },
- );
+ cx.new(|_| {
Self {
breakpoint_store,
worktree_store,
- scrollbar_state: ScrollbarState::new(list_state.clone()),
- list_state,
+ scrollbar_state,
+ // list_state,
breakpoints: Default::default(),
hide_scrollbar_task: None,
show_scrollbar: false,
workspace,
session,
- focus_handle: cx.focus_handle(),
+ focus_handle,
+ scroll_handle,
+ selected_ix: None,
+ }
+ })
+ }
+
+ fn edit_line_breakpoint(
+ &mut self,
+ path: Arc<Path>,
+ row: u32,
+ action: BreakpointEditAction,
+ cx: &mut Context<Self>,
+ ) {
+ self.breakpoint_store.update(cx, |breakpoint_store, cx| {
+ if let Some((buffer, breakpoint)) = breakpoint_store.breakpoint_at_row(&path, row, cx) {
+ breakpoint_store.toggle_breakpoint(buffer, breakpoint, action, cx);
+ } else {
+ log::error!("Couldn't find breakpoint at row event though it exists: row {row}")
+ }
+ })
+ }
+
+ fn go_to_line_breakpoint(
+ &mut self,
+ path: Arc<Path>,
+ row: u32,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let task = self
+ .worktree_store
+ .update(cx, |this, cx| this.find_or_create_worktree(path, false, cx));
+ cx.spawn_in(window, async move |this, cx| {
+ let (worktree, relative_path) = task.await?;
+ let worktree_id = worktree.update(cx, |this, _| this.id())?;
+ let item = this
+ .update_in(cx, |this, window, cx| {
+ this.workspace.update(cx, |this, cx| {
+ this.open_path((worktree_id, relative_path), None, true, window, cx)
+ })
+ })??
+ .await?;
+ if let Some(editor) = item.downcast::<Editor>() {
+ editor
+ .update_in(cx, |this, window, cx| {
+ this.go_to_singleton_buffer_point(Point { row, column: 0 }, window, cx);
+ })
+ .ok();
}
+ anyhow::Ok(())
})
+ .detach();
+ }
+
+ fn select_ix(&mut self, ix: Option<usize>, cx: &mut Context<Self>) {
+ self.selected_ix = ix;
+ if let Some(ix) = ix {
+ self.scroll_handle
+ .scroll_to_item(ix, ScrollStrategy::Center);
+ }
+ cx.notify();
+ }
+
+ fn select_next(&mut self, _: &menu::SelectNext, _window: &mut Window, cx: &mut Context<Self>) {
+ let ix = match self.selected_ix {
+ _ if self.breakpoints.len() == 0 => None,
+ None => Some(0),
+ Some(ix) => {
+ if ix == self.breakpoints.len() - 1 {
+ Some(0)
+ } else {
+ Some(ix + 1)
+ }
+ }
+ };
+ self.select_ix(ix, cx);
+ }
+
+ fn select_previous(
+ &mut self,
+ _: &menu::SelectPrevious,
+ _window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let ix = match self.selected_ix {
+ _ if self.breakpoints.len() == 0 => None,
+ None => Some(self.breakpoints.len() - 1),
+ Some(ix) => {
+ if ix == 0 {
+ Some(self.breakpoints.len() - 1)
+ } else {
+ Some(ix - 1)
+ }
+ }
+ };
+ self.select_ix(ix, cx);
+ }
+
+ fn select_first(
+ &mut self,
+ _: &menu::SelectFirst,
+ _window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let ix = if self.breakpoints.len() > 0 {
+ Some(0)
+ } else {
+ None
+ };
+ self.select_ix(ix, cx);
+ }
+
+ fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
+ let ix = if self.breakpoints.len() > 0 {
+ Some(self.breakpoints.len() - 1)
+ } else {
+ None
+ };
+ self.select_ix(ix, cx);
+ }
+
+ fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
+ let Some(entry) = self.selected_ix.and_then(|ix| self.breakpoints.get_mut(ix)) else {
+ return;
+ };
+
+ match &mut entry.kind {
+ BreakpointEntryKind::LineBreakpoint(line_breakpoint) => {
+ let path = line_breakpoint.breakpoint.path.clone();
+ let row = line_breakpoint.breakpoint.row;
+ self.go_to_line_breakpoint(path, row, window, cx);
+ }
+ BreakpointEntryKind::ExceptionBreakpoint(_) => {}
+ }
+ }
+
+ fn toggle_enable_breakpoint(
+ &mut self,
+ _: &ToggleEnableBreakpoint,
+ _window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let Some(entry) = self.selected_ix.and_then(|ix| self.breakpoints.get_mut(ix)) else {
+ return;
+ };
+
+ match &mut entry.kind {
+ BreakpointEntryKind::LineBreakpoint(line_breakpoint) => {
+ let path = line_breakpoint.breakpoint.path.clone();
+ let row = line_breakpoint.breakpoint.row;
+ self.edit_line_breakpoint(path, row, BreakpointEditAction::InvertState, cx);
+ }
+ BreakpointEntryKind::ExceptionBreakpoint(exception_breakpoint) => {
+ let id = exception_breakpoint.id.clone();
+ self.session.update(cx, |session, cx| {
+ session.toggle_exception_breakpoint(&id, cx);
+ });
+ }
+ }
+ cx.notify();
+ }
+
+ fn unset_breakpoint(
+ &mut self,
+ _: &UnsetBreakpoint,
+ _window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ let Some(entry) = self.selected_ix.and_then(|ix| self.breakpoints.get_mut(ix)) else {
+ return;
+ };
+
+ match &mut entry.kind {
+ BreakpointEntryKind::LineBreakpoint(line_breakpoint) => {
+ let path = line_breakpoint.breakpoint.path.clone();
+ let row = line_breakpoint.breakpoint.row;
+ self.edit_line_breakpoint(path, row, BreakpointEditAction::Toggle, cx);
+ }
+ BreakpointEntryKind::ExceptionBreakpoint(_) => {}
+ }
+ cx.notify();
}
fn hide_scrollbar(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -103,6 +273,30 @@ impl BreakpointList {
}))
}
+ fn render_list(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ let selected_ix = self.selected_ix;
+ let focus_handle = self.focus_handle.clone();
+ uniform_list(
+ cx.entity(),
+ "breakpoint-list",
+ self.breakpoints.len(),
+ move |this, range, window, cx| {
+ range
+ .clone()
+ .zip(&mut this.breakpoints[range])
+ .map(|(ix, breakpoint)| {
+ breakpoint
+ .render(ix, focus_handle.clone(), window, cx)
+ .toggle_state(Some(ix) == selected_ix)
+ .into_any_element()
+ })
+ .collect()
+ },
+ )
+ .track_scroll(self.scroll_handle.clone())
+ .flex_grow()
+ }
+
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Option<Stateful<Div>> {
if !(self.show_scrollbar || self.scrollbar_state.is_dragging()) {
return None;
@@ -142,12 +336,8 @@ impl BreakpointList {
}
}
impl Render for BreakpointList {
- fn render(
- &mut self,
- _window: &mut ui::Window,
- cx: &mut ui::Context<Self>,
- ) -> impl ui::IntoElement {
- let old_len = self.breakpoints.len();
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
+ // let old_len = self.breakpoints.len();
let breakpoints = self.breakpoint_store.read(cx).all_source_breakpoints(cx);
self.breakpoints.clear();
let weak = cx.weak_entity();
@@ -183,7 +373,7 @@ impl Render for BreakpointList {
.map(ToOwned::to_owned)
.map(SharedString::from)?;
let weak = weak.clone();
- let line = format!("Line {}", breakpoint.row + 1).into();
+ let line = breakpoint.row + 1;
Some(BreakpointEntry {
kind: BreakpointEntryKind::LineBreakpoint(LineBreakpoint {
name,
@@ -209,11 +399,9 @@ impl Render for BreakpointList {
});
self.breakpoints
.extend(breakpoints.chain(exception_breakpoints));
- if self.breakpoints.len() != old_len {
- self.list_state.reset(self.breakpoints.len());
- }
v_flex()
.id("breakpoint-list")
+ .key_context("BreakpointList")
.track_focus(&self.focus_handle)
.on_hover(cx.listener(|this, hovered, window, cx| {
if *hovered {
@@ -224,9 +412,16 @@ impl Render for BreakpointList {
this.hide_scrollbar(window, cx);
}
}))
+ .on_action(cx.listener(Self::select_next))
+ .on_action(cx.listener(Self::select_previous))
+ .on_action(cx.listener(Self::select_first))
+ .on_action(cx.listener(Self::select_last))
+ .on_action(cx.listener(Self::confirm))
+ .on_action(cx.listener(Self::toggle_enable_breakpoint))
+ .on_action(cx.listener(Self::unset_breakpoint))
.size_full()
.m_0p5()
- .child(list(self.list_state.clone()).flex_grow())
+ .child(self.render_list(window, cx))
.children(self.render_vertical_scrollbar(cx))
}
}
@@ -234,55 +429,58 @@ impl Render for BreakpointList {
struct LineBreakpoint {
name: SharedString,
dir: Option<SharedString>,
- line: SharedString,
+ line: u32,
breakpoint: SourceBreakpoint,
}
impl LineBreakpoint {
- fn render(self, weak: WeakEntity<BreakpointList>) -> ListItem {
- let LineBreakpoint {
- name,
- dir,
- line,
- breakpoint,
- } = self;
- let icon_name = if breakpoint.state.is_enabled() {
+ fn render(
+ &mut self,
+ ix: usize,
+ focus_handle: FocusHandle,
+ weak: WeakEntity<BreakpointList>,
+ ) -> ListItem {
+ let icon_name = if self.breakpoint.state.is_enabled() {
IconName::DebugBreakpoint
} else {
IconName::DebugDisabledBreakpoint
};
- let path = breakpoint.path;
- let row = breakpoint.row;
+ let path = self.breakpoint.path.clone();
+ let row = self.breakpoint.row;
+ let is_enabled = self.breakpoint.state.is_enabled();
let indicator = div()
.id(SharedString::from(format!(
"breakpoint-ui-toggle-{:?}/{}:{}",
- dir, name, line
+ self.dir, self.name, self.line
)))
.cursor_pointer()
- .tooltip(Tooltip::text(if breakpoint.state.is_enabled() {
- "Disable Breakpoint"
- } else {
- "Enable Breakpoint"
- }))
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ if is_enabled {
+ "Disable Breakpoint"
+ } else {
+ "Enable Breakpoint"
+ },
+ &ToggleEnableBreakpoint,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
+ })
.on_click({
let weak = weak.clone();
let path = path.clone();
move |_, _, cx| {
- weak.update(cx, |this, cx| {
- this.breakpoint_store.update(cx, |this, cx| {
- if let Some((buffer, breakpoint)) =
- this.breakpoint_at_row(&path, row, cx)
- {
- this.toggle_breakpoint(
- buffer,
- breakpoint,
- BreakpointEditAction::InvertState,
- cx,
- );
- } else {
- log::error!("Couldn't find breakpoint at row event though it exists: row {row}")
- }
- })
+ weak.update(cx, |breakpoint_list, cx| {
+ breakpoint_list.edit_line_breakpoint(
+ path.clone(),
+ row,
+ BreakpointEditAction::InvertState,
+ cx,
+ );
})
.ok();
}
@@ -291,8 +489,17 @@ impl LineBreakpoint {
.on_mouse_down(MouseButton::Left, move |_, _, _| {});
ListItem::new(SharedString::from(format!(
"breakpoint-ui-item-{:?}/{}:{}",
- dir, name, line
+ self.dir, self.name, self.line
)))
+ .on_click({
+ let weak = weak.clone();
+ move |_, _, cx| {
+ weak.update(cx, |breakpoint_list, cx| {
+ breakpoint_list.select_ix(Some(ix), cx);
+ })
+ .ok();
+ }
+ })
.start_slot(indicator)
.rounded()
.on_secondary_mouse_down(|_, _, cx| {
@@ -302,7 +509,7 @@ impl LineBreakpoint {
IconButton::new(
SharedString::from(format!(
"breakpoint-ui-on-click-go-to-line-remove-{:?}/{}:{}",
- dir, name, line
+ self.dir, self.name, self.line
)),
IconName::Close,
)
@@ -310,103 +517,60 @@ impl LineBreakpoint {
let weak = weak.clone();
let path = path.clone();
move |_, _, cx| {
- weak.update(cx, |this, cx| {
- this.breakpoint_store.update(cx, |this, cx| {
- if let Some((buffer, breakpoint)) =
- this.breakpoint_at_row(&path, row, cx)
- {
- this.toggle_breakpoint(
- buffer,
- breakpoint,
- BreakpointEditAction::Toggle,
- cx,
- );
- } else {
- log::error!("Couldn't find breakpoint at row event though it exists: row {row}")
- }
- })
+ weak.update(cx, |breakpoint_list, cx| {
+ breakpoint_list.edit_line_breakpoint(
+ path.clone(),
+ row,
+ BreakpointEditAction::Toggle,
+ cx,
+ );
})
.ok();
}
})
- .icon_size(ui::IconSize::XSmall),
+ .tooltip(move |window, cx| {
+ Tooltip::for_action_in(
+ "Unset Breakpoint",
+ &UnsetBreakpoint,
+ &focus_handle,
+ window,
+ cx,
+ )
+ })
+ .icon_size(ui::IconSize::Indicator),
)
.child(
v_flex()
+ .py_1()
+ .gap_1()
+ .min_h(px(22.))
+ .justify_center()
.id(SharedString::from(format!(
"breakpoint-ui-on-click-go-to-line-{:?}/{}:{}",
- dir, name, line
+ self.dir, self.name, self.line
)))
.on_click(move |_, window, cx| {
- let path = path.clone();
- let weak = weak.clone();
- let row = breakpoint.row;
- maybe!({
- let task = weak
- .update(cx, |this, cx| {
- this.worktree_store.update(cx, |this, cx| {
- this.find_or_create_worktree(path, false, cx)
- })
- })
- .ok()?;
- window
- .spawn(cx, async move |cx| {
- let (worktree, relative_path) = task.await?;
- let worktree_id = worktree.update(cx, |this, _| this.id())?;
- let item = weak
- .update_in(cx, |this, window, cx| {
- this.workspace.update(cx, |this, cx| {
- this.open_path(
- (worktree_id, relative_path),
- None,
- true,
- window,
- cx,
- )
- })
- })??
- .await?;
- if let Some(editor) = item.downcast::<Editor>() {
- editor
- .update_in(cx, |this, window, cx| {
- this.go_to_singleton_buffer_point(
- Point { row, column: 0 },
- window,
- cx,
- );
- })
- .ok();
- }
- anyhow::Ok(())
- })
- .detach();
-
- Some(())
- });
+ weak.update(cx, |breakpoint_list, cx| {
+ breakpoint_list.select_ix(Some(ix), cx);
+ breakpoint_list.go_to_line_breakpoint(path.clone(), row, window, cx);
+ })
+ .ok();
})
.cursor_pointer()
- .py_1()
- .items_center()
.child(
h_flex()
.gap_1()
.child(
- Label::new(name)
+ Label::new(format!("{}:{}", self.name, self.line))
.size(LabelSize::Small)
.line_height_style(ui::LineHeightStyle::UiLabel),
)
- .children(dir.map(|dir| {
+ .children(self.dir.clone().map(|dir| {
Label::new(dir)
.color(Color::Muted)
.size(LabelSize::Small)
.line_height_style(ui::LineHeightStyle::UiLabel)
})),
- )
- .child(
- Label::new(line)
- .size(LabelSize::XSmall)
- .color(Color::Muted)
- .line_height_style(ui::LineHeightStyle::UiLabel),
),
)
}
@@ -419,17 +583,31 @@ struct ExceptionBreakpoint {
}
impl ExceptionBreakpoint {
- fn render(self, list: WeakEntity<BreakpointList>) -> ListItem {
+ fn render(
+ &mut self,
+ ix: usize,
+ focus_handle: FocusHandle,
+ list: WeakEntity<BreakpointList>,
+ ) -> ListItem {
let color = if self.is_enabled {
Color::Debugger
} else {
Color::Muted
};
let id = SharedString::from(&self.id);
+ let is_enabled = self.is_enabled;
+
ListItem::new(SharedString::from(format!(
"exception-breakpoint-ui-item-{}",
self.id
)))
+ .on_click({
+ let list = list.clone();
+ move |_, _, cx| {
+ list.update(cx, |list, cx| list.select_ix(Some(ix), cx))
+ .ok();
+ }
+ })
.rounded()
.on_secondary_mouse_down(|_, _, cx| {
cx.stop_propagation();
@@ -440,38 +618,49 @@ impl ExceptionBreakpoint {
"exception-breakpoint-ui-item-{}-click-handler",
self.id
)))
- .tooltip(Tooltip::text(if self.is_enabled {
- "Disable Exception Breakpoint"
- } else {
- "Enable Exception Breakpoint"
- }))
- .on_click(move |_, _, cx| {
- list.update(cx, |this, cx| {
- this.session.update(cx, |this, cx| {
- this.toggle_exception_breakpoint(&id, cx);
- });
- cx.notify();
- })
- .ok();
+ .tooltip(move |window, cx| {
+ Tooltip::for_action_in(
+ if is_enabled {
+ "Disable Exception Breakpoint"
+ } else {
+ "Enable Exception Breakpoint"
+ },
+ &ToggleEnableBreakpoint,
+ &focus_handle,
+ window,
+ cx,
+ )
+ })
+ .on_click({
+ let list = list.clone();
+ move |_, _, cx| {
+ list.update(cx, |this, cx| {
+ this.session.update(cx, |this, cx| {
+ this.toggle_exception_breakpoint(&id, cx);
+ });
+ cx.notify();
+ })
+ .ok();
+ }
})
.cursor_pointer()
.child(Indicator::icon(Icon::new(IconName::Flame)).color(color)),
)
.child(
- div()
+ v_flex()
.py_1()
.gap_1()
+ .min_h(px(22.))
+ .justify_center()
+ .id(("exception-breakpoint-label", ix))
.child(
- Label::new(self.data.label)
+ Label::new(self.data.label.clone())
.size(LabelSize::Small)
.line_height_style(ui::LineHeightStyle::UiLabel),
)
- .children(self.data.description.map(|description| {
- Label::new(description)
- .size(LabelSize::XSmall)
- .line_height_style(ui::LineHeightStyle::UiLabel)
- .color(Color::Muted)
- })),
+ .when_some(self.data.description.clone(), |el, description| {
+ el.tooltip(Tooltip::text(description))
+ }),
)
}
}
@@ -486,14 +675,21 @@ struct BreakpointEntry {
kind: BreakpointEntryKind,
weak: WeakEntity<BreakpointList>,
}
-impl RenderOnce for BreakpointEntry {
- fn render(self, _: &mut ui::Window, _: &mut App) -> impl ui::IntoElement {
- match self.kind {
+
+impl BreakpointEntry {
+ fn render(
+ &mut self,
+ ix: usize,
+ focus_handle: FocusHandle,
+ _: &mut Window,
+ _: &mut App,
+ ) -> ListItem {
+ match &mut self.kind {
BreakpointEntryKind::LineBreakpoint(line_breakpoint) => {
- line_breakpoint.render(self.weak)
+ line_breakpoint.render(ix, focus_handle, self.weak.clone())
}
BreakpointEntryKind::ExceptionBreakpoint(exception_breakpoint) => {
- exception_breakpoint.render(self.weak)
+ exception_breakpoint.render(ix, focus_handle, self.weak.clone())
}
}
}
@@ -339,3 +339,5 @@ pub mod outline {
actions!(zed_predict_onboarding, [OpenZedPredictOnboarding]);
actions!(git_onboarding, [OpenGitIntegrationOnboarding]);
+
+actions!(debugger, [ToggleEnableBreakpoint, UnsetBreakpoint]);