debugger: Surface validity of breakpoints (#30380)

We now show on the breakpoint itself whether it can ever be hit.

![image](https://github.com/user-attachments/assets/148d7712-53c9-4a0a-9fc0-4ff80dec5fb1)

Release Notes:

- N/A

---------

Signed-off-by: Umesh Yadav <git@umesh.dev>
Co-authored-by: Anthony <anthony@zed.dev>
Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: Michael Sloan <michael@zed.dev>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
Co-authored-by: Ben Kunkle <ben@zed.dev>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
Co-authored-by: peppidesu <bakker.pepijn@gmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
Co-authored-by: Jens Krause <47693+sectore@users.noreply.github.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
Co-authored-by: Max Nordlund <max.nordlund@gmail.com>
Co-authored-by: Finn Evers <dev@bahn.sh>
Co-authored-by: tidely <43219534+tidely@users.noreply.github.com>
Co-authored-by: Sergei Kartsev <kartsevsb@gmail.com>
Co-authored-by: Shardul Vaidya <31039336+5herlocked@users.noreply.github.com>
Co-authored-by: Chris Kelly <amateurhuman@gmail.com>
Co-authored-by: Peter Tripp <peter@zed.dev>
Co-authored-by: Umesh Yadav <23421535+imumesh18@users.noreply.github.com>
Co-authored-by: Julia Ryan <juliaryan3.14@gmail.com>
Co-authored-by: Cole Miller <m@cole-miller.net>
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: william341 <wwokwilliam@gmail.com>
Co-authored-by: Liam <33645555+lj3954@users.noreply.github.com>
Co-authored-by: AidanV <aidanvanduyne@gmail.com>
Co-authored-by: imumesh18 <umesh4257@gmail.com>
Co-authored-by: d1y <chenhonzhou@gmail.com>
Co-authored-by: AidanV <84053180+AidanV@users.noreply.github.com>
Co-authored-by: Anthony Eid <56899983+Anthony-Eid@users.noreply.github.com>
Co-authored-by: 张小白 <364772080@qq.com>
Co-authored-by: THELOSTSOUL <1095533751@qq.com>
Co-authored-by: Ron Harel <55725807+ronharel02@users.noreply.github.com>
Co-authored-by: Tristan Hume <tristan@anthropic.com>
Co-authored-by: Stanislav Alekseev <43210583+WeetHet@users.noreply.github.com>
Co-authored-by: Joseph T. Lyons <JosephTLyons@gmail.com>
Co-authored-by: Remco Smits <djsmits12@gmail.com>
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Oleksiy Syvokon <oleksiy@zed.dev>
Co-authored-by: Thomas David Baker <bakert@gmail.com>
Co-authored-by: Nate Butler <iamnbutler@gmail.com>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: Rob McBroom <github@skurfer.com>
Co-authored-by: CharlesChen0823 <yongchen0823@gmail.com>
This commit is contained in:
Piotr Osiewicz
2025-05-20 15:56:15 +00:00
committed by GitHub
co-authored by Anthony Cole Miller Michael Sloan Marshall Bowers Ben Kunkle Danilo Leal Agus Zubiaga Ben Brandt Agus Zubiaga Danilo Leal Richard Feldman Max Brunsfeld Smit Barmase peppidesu Kirill Bulatov Ben Kunkle Jens Krause Bennet Bo Fenner Max Nordlund Finn Evers tidely Sergei Kartsev Shardul Vaidya Chris Kelly Peter Tripp Umesh Yadav Julia Ryan Cole Miller Conrad Irwin william341 Liam AidanV imumesh18 d1y AidanV Anthony Eid 张小白 THELOSTSOUL Ron Harel Tristan Hume Stanislav Alekseev Joseph T. Lyons Remco Smits Anthony Eid Oleksiy Syvokon Thomas David Baker Nate Butler Mikayla Maki Rob McBroom CharlesChen0823
parent 36ae564b61
commit 17cf04558b
11 changed files with 439 additions and 237 deletions
+35 -23
View File
@@ -122,10 +122,11 @@ use markdown::Markdown;
use mouse_context_menu::MouseContextMenu;
use persistence::DB;
use project::{
ProjectPath,
BreakpointWithPosition, ProjectPath,
debugger::{
breakpoint_store::{
BreakpointEditAction, BreakpointState, BreakpointStore, BreakpointStoreEvent,
BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore,
BreakpointStoreEvent,
},
session::{Session, SessionEvent},
},
@@ -198,7 +199,7 @@ use theme::{
};
use ui::{
ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape, IconName,
IconSize, Key, Tooltip, h_flex, prelude::*,
IconSize, Indicator, Key, Tooltip, h_flex, prelude::*,
};
use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
use workspace::{
@@ -6997,7 +6998,7 @@ impl Editor {
range: Range<DisplayRow>,
window: &mut Window,
cx: &mut Context<Self>,
) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
let mut breakpoint_display_points = HashMap::default();
let Some(breakpoint_store) = self.breakpoint_store.clone() else {
@@ -7031,15 +7032,17 @@ impl Editor {
buffer_snapshot,
cx,
);
for (anchor, breakpoint) in breakpoints {
for (breakpoint, state) in breakpoints {
let multi_buffer_anchor =
Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), *anchor);
Anchor::in_buffer(excerpt_id, buffer_snapshot.remote_id(), breakpoint.position);
let position = multi_buffer_anchor
.to_point(&multi_buffer_snapshot)
.to_display_point(&snapshot);
breakpoint_display_points
.insert(position.row(), (multi_buffer_anchor, breakpoint.clone()));
breakpoint_display_points.insert(
position.row(),
(multi_buffer_anchor, breakpoint.bp.clone(), state),
);
}
}
@@ -7214,8 +7217,10 @@ impl Editor {
position: Anchor,
row: DisplayRow,
breakpoint: &Breakpoint,
state: Option<BreakpointSessionState>,
cx: &mut Context<Self>,
) -> IconButton {
let is_rejected = state.is_some_and(|s| !s.verified);
// Is it a breakpoint that shows up when hovering over gutter?
let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
(false, false),
@@ -7241,6 +7246,8 @@ impl Editor {
let color = if is_phantom {
Color::Hint
} else if is_rejected {
Color::Disabled
} else {
Color::Debugger
};
@@ -7268,9 +7275,18 @@ impl Editor {
}
let primary_text = SharedString::from(primary_text);
let focus_handle = self.focus_handle.clone();
let meta = if is_rejected {
"No executable code is associated with this line."
} else {
"Right-click for more options."
};
IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
.icon_size(IconSize::XSmall)
.size(ui::ButtonSize::None)
.when(is_rejected, |this| {
this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
})
.icon_color(color)
.style(ButtonStyle::Transparent)
.on_click(cx.listener({
@@ -7302,14 +7318,7 @@ impl Editor {
);
}))
.tooltip(move |window, cx| {
Tooltip::with_meta_in(
primary_text.clone(),
None,
"Right-click for more options",
&focus_handle,
window,
cx,
)
Tooltip::with_meta_in(primary_text.clone(), None, meta, &focus_handle, window, cx)
})
}
@@ -7449,11 +7458,11 @@ impl Editor {
_style: &EditorStyle,
is_active: bool,
row: DisplayRow,
breakpoint: Option<(Anchor, Breakpoint)>,
breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
cx: &mut Context<Self>,
) -> IconButton {
let color = Color::Muted;
let position = breakpoint.as_ref().map(|(anchor, _)| *anchor);
let position = breakpoint.as_ref().map(|(anchor, _, _)| *anchor);
IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
.shape(ui::IconButtonShape::Square)
@@ -9633,16 +9642,16 @@ impl Editor {
cx,
)
.next()
.and_then(|(anchor, bp)| {
.and_then(|(bp, _)| {
let breakpoint_row = buffer_snapshot
.summary_for_anchor::<text::PointUtf16>(anchor)
.summary_for_anchor::<text::PointUtf16>(&bp.position)
.row;
if breakpoint_row == row {
snapshot
.buffer_snapshot
.anchor_in_excerpt(enclosing_excerpt, *anchor)
.map(|anchor| (anchor, bp.clone()))
.anchor_in_excerpt(enclosing_excerpt, bp.position)
.map(|position| (position, bp.bp.clone()))
} else {
None
}
@@ -9805,7 +9814,10 @@ impl Editor {
breakpoint_store.update(cx, |breakpoint_store, cx| {
breakpoint_store.toggle_breakpoint(
buffer,
(breakpoint_position.text_anchor, breakpoint),
BreakpointWithPosition {
position: breakpoint_position.text_anchor,
bp: breakpoint,
},
edit_action,
cx,
);
+11 -11
View File
@@ -18716,7 +18716,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18741,7 +18741,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18763,7 +18763,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18830,7 +18830,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18851,7 +18851,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18871,7 +18871,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18894,7 +18894,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -18917,7 +18917,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -19010,7 +19010,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -19042,7 +19042,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
@@ -19078,7 +19078,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
.as_ref()
.unwrap()
.read(cx)
.all_breakpoints(cx)
.all_source_breakpoints(cx)
.clone()
});
+10 -8
View File
@@ -62,7 +62,7 @@ use multi_buffer::{
use project::{
ProjectPath,
debugger::breakpoint_store::Breakpoint,
debugger::breakpoint_store::{Breakpoint, BreakpointSessionState},
project_settings::{GitGutterSetting, GitHunkStyleSetting, ProjectSettings},
};
use settings::Settings;
@@ -2317,7 +2317,7 @@ impl EditorElement {
gutter_hitbox: &Hitbox,
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
snapshot: &EditorSnapshot,
breakpoints: HashMap<DisplayRow, (Anchor, Breakpoint)>,
breakpoints: HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)>,
row_infos: &[RowInfo],
window: &mut Window,
cx: &mut App,
@@ -2325,7 +2325,7 @@ impl EditorElement {
self.editor.update(cx, |editor, cx| {
breakpoints
.into_iter()
.filter_map(|(display_row, (text_anchor, bp))| {
.filter_map(|(display_row, (text_anchor, bp, state))| {
if row_infos
.get((display_row.0.saturating_sub(range.start.0)) as usize)
.is_some_and(|row_info| {
@@ -2348,7 +2348,7 @@ impl EditorElement {
return None;
}
let button = editor.render_breakpoint(text_anchor, display_row, &bp, cx);
let button = editor.render_breakpoint(text_anchor, display_row, &bp, state, cx);
let button = prepaint_gutter_button(
button,
@@ -2378,7 +2378,7 @@ impl EditorElement {
gutter_hitbox: &Hitbox,
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
snapshot: &EditorSnapshot,
breakpoints: &mut HashMap<DisplayRow, (Anchor, Breakpoint)>,
breakpoints: &mut HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)>,
window: &mut Window,
cx: &mut App,
) -> Vec<AnyElement> {
@@ -7437,8 +7437,10 @@ impl Element for EditorElement {
editor.active_breakpoints(start_row..end_row, window, cx)
});
if cx.has_flag::<DebuggerFeatureFlag>() {
for display_row in breakpoint_rows.keys() {
active_rows.entry(*display_row).or_default().breakpoint = true;
for (display_row, (_, bp, state)) in &breakpoint_rows {
if bp.is_enabled() && state.is_none_or(|s| s.verified) {
active_rows.entry(*display_row).or_default().breakpoint = true;
}
}
}
@@ -7478,7 +7480,7 @@ impl Element for EditorElement {
let breakpoint = Breakpoint::new_standard();
phantom_breakpoint.collides_with_existing_breakpoint =
false;
(position, breakpoint)
(position, breakpoint, None)
});
}
})