debugger: Add support for setting multiple breakpoints via actions (#28437)

Allow setting multiple breakpoints with multi cursors

Release Notes:

- N/A

---------

Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Anthony <anthony@zed.dev>
This commit is contained in:
Piotr Osiewicz 2025-04-11 05:31:57 +02:00 committed by GitHub
parent 5757e352b0
commit c35238bd72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 93 additions and 64 deletions

View file

@ -8854,15 +8854,6 @@ impl Editor {
});
}
fn breakpoint_at_cursor_head(
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Option<(Anchor, Breakpoint)> {
let cursor_position: Point = self.selections.newest(cx).head();
self.breakpoint_at_row(cursor_position.row, window, cx)
}
pub(crate) fn breakpoint_at_row(
&self,
row: u32,
@ -8872,6 +8863,15 @@ impl Editor {
let snapshot = self.snapshot(window, cx);
let breakpoint_position = snapshot.buffer_snapshot.anchor_before(Point::new(row, 0));
self.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
}
pub(crate) fn breakpoint_at_anchor(
&self,
breakpoint_position: Anchor,
snapshot: &EditorSnapshot,
cx: &mut Context<Self>,
) -> Option<(Anchor, Breakpoint)> {
let project = self.project.clone()?;
let buffer_id = breakpoint_position.buffer_id.or_else(|| {
@ -8929,29 +8929,51 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let (anchor, bp) = self
.breakpoint_at_cursor_head(window, cx)
.unwrap_or_else(|| {
let cursor_position: Point = self.selections.newest(cx).head();
for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
let breakpoint = breakpoint.unwrap_or_else(|| Breakpoint {
message: None,
state: BreakpointState::Enabled,
condition: None,
hit_condition: None,
});
let breakpoint_position = self
.snapshot(window, cx)
self.add_edit_breakpoint_block(
anchor,
&breakpoint,
BreakpointPromptEditAction::Log,
window,
cx,
);
}
}
fn breakpoints_at_cursors(
&self,
window: &mut Window,
cx: &mut Context<Self>,
) -> Vec<(Anchor, Option<Breakpoint>)> {
let snapshot = self.snapshot(window, cx);
let cursors = self
.selections
.disjoint_anchors()
.into_iter()
.map(|selection| {
let cursor_position: Point = selection.head().to_point(&snapshot.buffer_snapshot);
let breakpoint_position = snapshot
.display_snapshot
.buffer_snapshot
.anchor_after(Point::new(cursor_position.row, 0));
let breakpoint = self
.breakpoint_at_anchor(breakpoint_position, &snapshot, cx)
.map(|(anchor, breakpoint)| (anchor, Some(breakpoint)));
(
breakpoint_position,
Breakpoint {
message: None,
state: BreakpointState::Enabled,
condition: None,
hit_condition: None,
},
)
});
breakpoint.unwrap_or_else(|| (breakpoint_position, None))
})
// There might be multiple cursors on the same line; all of them should have the same anchors though as their breakpoints positions, which makes it possible to sort and dedup the list.
.collect::<HashMap<Anchor, _>>();
self.add_edit_breakpoint_block(anchor, &bp, BreakpointPromptEditAction::Log, window, cx);
cursors.into_iter().collect()
}
pub fn enable_breakpoint(
@ -8960,15 +8982,16 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
if breakpoint.is_disabled() {
self.edit_breakpoint_at_anchor(
anchor,
breakpoint,
BreakpointEditAction::InvertState,
cx,
);
}
for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_disabled()) else {
continue;
};
self.edit_breakpoint_at_anchor(
anchor,
breakpoint,
BreakpointEditAction::InvertState,
cx,
);
}
}
@ -8978,15 +9001,16 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
if breakpoint.is_enabled() {
self.edit_breakpoint_at_anchor(
anchor,
breakpoint,
BreakpointEditAction::InvertState,
cx,
);
}
for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
let Some(breakpoint) = breakpoint.filter(|breakpoint| breakpoint.is_enabled()) else {
continue;
};
self.edit_breakpoint_at_anchor(
anchor,
breakpoint,
BreakpointEditAction::InvertState,
cx,
);
}
}
@ -8996,25 +9020,22 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
let edit_action = BreakpointEditAction::Toggle;
if let Some((anchor, breakpoint)) = self.breakpoint_at_cursor_head(window, cx) {
self.edit_breakpoint_at_anchor(anchor, breakpoint, edit_action, cx);
} else {
let cursor_position: Point = self.selections.newest(cx).head();
let breakpoint_position = self
.snapshot(window, cx)
.display_snapshot
.buffer_snapshot
.anchor_after(Point::new(cursor_position.row, 0));
self.edit_breakpoint_at_anchor(
breakpoint_position,
Breakpoint::new_standard(),
edit_action,
cx,
);
for (anchor, breakpoint) in self.breakpoints_at_cursors(window, cx) {
if let Some(breakpoint) = breakpoint {
self.edit_breakpoint_at_anchor(
anchor,
breakpoint,
BreakpointEditAction::Toggle,
cx,
);
} else {
self.edit_breakpoint_at_anchor(
anchor,
Breakpoint::new_standard(),
BreakpointEditAction::Toggle,
cx,
);
}
}
}

View file

@ -17999,7 +17999,15 @@ fn add_log_breakpoint_at_cursor(
cx: &mut Context<Editor>,
) {
let (anchor, bp) = editor
.breakpoint_at_cursor_head(window, cx)
.breakpoints_at_cursors(window, cx)
.first()
.and_then(|(anchor, bp)| {
if let Some(bp) = bp {
Some((*anchor, bp.clone()))
} else {
None
}
})
.unwrap_or_else(|| {
let cursor_position: Point = editor.selections.newest(cx).head();