debugger: Surface validity of breakpoints (#30380)
We now show on the breakpoint itself whether it can ever be hit.  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:
parent
36ae564b61
commit
17cf04558b
11 changed files with 439 additions and 237 deletions
|
@ -2517,7 +2517,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
||||||
|
@ -2526,7 +2526,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2550,7 +2550,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
||||||
|
@ -2559,7 +2559,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2583,7 +2583,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
||||||
|
@ -2592,7 +2592,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2616,7 +2616,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
let breakpoints_b = editor_b.update(cx_b, |editor, cx| {
|
||||||
|
@ -2625,7 +2625,7 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ impl Render for BreakpointList {
|
||||||
cx: &mut ui::Context<Self>,
|
cx: &mut ui::Context<Self>,
|
||||||
) -> impl ui::IntoElement {
|
) -> impl ui::IntoElement {
|
||||||
let old_len = self.breakpoints.len();
|
let old_len = self.breakpoints.len();
|
||||||
let breakpoints = self.breakpoint_store.read(cx).all_breakpoints(cx);
|
let breakpoints = self.breakpoint_store.read(cx).all_source_breakpoints(cx);
|
||||||
self.breakpoints.clear();
|
self.breakpoints.clear();
|
||||||
let weak = cx.weak_entity();
|
let weak = cx.weak_entity();
|
||||||
let breakpoints = breakpoints.into_iter().flat_map(|(path, mut breakpoints)| {
|
let breakpoints = breakpoints.into_iter().flat_map(|(path, mut breakpoints)| {
|
||||||
|
|
|
@ -122,10 +122,11 @@ use markdown::Markdown;
|
||||||
use mouse_context_menu::MouseContextMenu;
|
use mouse_context_menu::MouseContextMenu;
|
||||||
use persistence::DB;
|
use persistence::DB;
|
||||||
use project::{
|
use project::{
|
||||||
ProjectPath,
|
BreakpointWithPosition, ProjectPath,
|
||||||
debugger::{
|
debugger::{
|
||||||
breakpoint_store::{
|
breakpoint_store::{
|
||||||
BreakpointEditAction, BreakpointState, BreakpointStore, BreakpointStoreEvent,
|
BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore,
|
||||||
|
BreakpointStoreEvent,
|
||||||
},
|
},
|
||||||
session::{Session, SessionEvent},
|
session::{Session, SessionEvent},
|
||||||
},
|
},
|
||||||
|
@ -198,7 +199,7 @@ use theme::{
|
||||||
};
|
};
|
||||||
use ui::{
|
use ui::{
|
||||||
ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape, IconName,
|
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 util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
|
@ -6997,7 +6998,7 @@ impl Editor {
|
||||||
range: Range<DisplayRow>,
|
range: Range<DisplayRow>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> HashMap<DisplayRow, (Anchor, Breakpoint)> {
|
) -> HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)> {
|
||||||
let mut breakpoint_display_points = HashMap::default();
|
let mut breakpoint_display_points = HashMap::default();
|
||||||
|
|
||||||
let Some(breakpoint_store) = self.breakpoint_store.clone() else {
|
let Some(breakpoint_store) = self.breakpoint_store.clone() else {
|
||||||
|
@ -7031,15 +7032,17 @@ impl Editor {
|
||||||
buffer_snapshot,
|
buffer_snapshot,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
for (anchor, breakpoint) in breakpoints {
|
for (breakpoint, state) in breakpoints {
|
||||||
let multi_buffer_anchor =
|
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
|
let position = multi_buffer_anchor
|
||||||
.to_point(&multi_buffer_snapshot)
|
.to_point(&multi_buffer_snapshot)
|
||||||
.to_display_point(&snapshot);
|
.to_display_point(&snapshot);
|
||||||
|
|
||||||
breakpoint_display_points
|
breakpoint_display_points.insert(
|
||||||
.insert(position.row(), (multi_buffer_anchor, breakpoint.clone()));
|
position.row(),
|
||||||
|
(multi_buffer_anchor, breakpoint.bp.clone(), state),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7214,8 +7217,10 @@ impl Editor {
|
||||||
position: Anchor,
|
position: Anchor,
|
||||||
row: DisplayRow,
|
row: DisplayRow,
|
||||||
breakpoint: &Breakpoint,
|
breakpoint: &Breakpoint,
|
||||||
|
state: Option<BreakpointSessionState>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> IconButton {
|
) -> IconButton {
|
||||||
|
let is_rejected = state.is_some_and(|s| !s.verified);
|
||||||
// Is it a breakpoint that shows up when hovering over gutter?
|
// Is it a breakpoint that shows up when hovering over gutter?
|
||||||
let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
|
let (is_phantom, collides_with_existing) = self.gutter_breakpoint_indicator.0.map_or(
|
||||||
(false, false),
|
(false, false),
|
||||||
|
@ -7241,6 +7246,8 @@ impl Editor {
|
||||||
|
|
||||||
let color = if is_phantom {
|
let color = if is_phantom {
|
||||||
Color::Hint
|
Color::Hint
|
||||||
|
} else if is_rejected {
|
||||||
|
Color::Disabled
|
||||||
} else {
|
} else {
|
||||||
Color::Debugger
|
Color::Debugger
|
||||||
};
|
};
|
||||||
|
@ -7268,9 +7275,18 @@ impl Editor {
|
||||||
}
|
}
|
||||||
let primary_text = SharedString::from(primary_text);
|
let primary_text = SharedString::from(primary_text);
|
||||||
let focus_handle = self.focus_handle.clone();
|
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)
|
IconButton::new(("breakpoint_indicator", row.0 as usize), icon)
|
||||||
.icon_size(IconSize::XSmall)
|
.icon_size(IconSize::XSmall)
|
||||||
.size(ui::ButtonSize::None)
|
.size(ui::ButtonSize::None)
|
||||||
|
.when(is_rejected, |this| {
|
||||||
|
this.indicator(Indicator::icon(Icon::new(IconName::Warning)).color(Color::Warning))
|
||||||
|
})
|
||||||
.icon_color(color)
|
.icon_color(color)
|
||||||
.style(ButtonStyle::Transparent)
|
.style(ButtonStyle::Transparent)
|
||||||
.on_click(cx.listener({
|
.on_click(cx.listener({
|
||||||
|
@ -7302,14 +7318,7 @@ impl Editor {
|
||||||
);
|
);
|
||||||
}))
|
}))
|
||||||
.tooltip(move |window, cx| {
|
.tooltip(move |window, cx| {
|
||||||
Tooltip::with_meta_in(
|
Tooltip::with_meta_in(primary_text.clone(), None, meta, &focus_handle, window, cx)
|
||||||
primary_text.clone(),
|
|
||||||
None,
|
|
||||||
"Right-click for more options",
|
|
||||||
&focus_handle,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7449,11 +7458,11 @@ impl Editor {
|
||||||
_style: &EditorStyle,
|
_style: &EditorStyle,
|
||||||
is_active: bool,
|
is_active: bool,
|
||||||
row: DisplayRow,
|
row: DisplayRow,
|
||||||
breakpoint: Option<(Anchor, Breakpoint)>,
|
breakpoint: Option<(Anchor, Breakpoint, Option<BreakpointSessionState>)>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> IconButton {
|
) -> IconButton {
|
||||||
let color = Color::Muted;
|
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)
|
IconButton::new(("run_indicator", row.0 as usize), ui::IconName::Play)
|
||||||
.shape(ui::IconButtonShape::Square)
|
.shape(ui::IconButtonShape::Square)
|
||||||
|
@ -9633,16 +9642,16 @@ impl Editor {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
.next()
|
.next()
|
||||||
.and_then(|(anchor, bp)| {
|
.and_then(|(bp, _)| {
|
||||||
let breakpoint_row = buffer_snapshot
|
let breakpoint_row = buffer_snapshot
|
||||||
.summary_for_anchor::<text::PointUtf16>(anchor)
|
.summary_for_anchor::<text::PointUtf16>(&bp.position)
|
||||||
.row;
|
.row;
|
||||||
|
|
||||||
if breakpoint_row == row {
|
if breakpoint_row == row {
|
||||||
snapshot
|
snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.anchor_in_excerpt(enclosing_excerpt, *anchor)
|
.anchor_in_excerpt(enclosing_excerpt, bp.position)
|
||||||
.map(|anchor| (anchor, bp.clone()))
|
.map(|position| (position, bp.bp.clone()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -9805,7 +9814,10 @@ impl Editor {
|
||||||
breakpoint_store.update(cx, |breakpoint_store, cx| {
|
breakpoint_store.update(cx, |breakpoint_store, cx| {
|
||||||
breakpoint_store.toggle_breakpoint(
|
breakpoint_store.toggle_breakpoint(
|
||||||
buffer,
|
buffer,
|
||||||
(breakpoint_position.text_anchor, breakpoint),
|
BreakpointWithPosition {
|
||||||
|
position: breakpoint_position.text_anchor,
|
||||||
|
bp: breakpoint,
|
||||||
|
},
|
||||||
edit_action,
|
edit_action,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
|
@ -18716,7 +18716,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18741,7 +18741,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18763,7 +18763,7 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18830,7 +18830,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18851,7 +18851,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18871,7 +18871,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18894,7 +18894,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18917,7 +18917,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19010,7 +19010,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19042,7 +19042,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19078,7 +19078,7 @@ async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.all_breakpoints(cx)
|
.all_source_breakpoints(cx)
|
||||||
.clone()
|
.clone()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ use multi_buffer::{
|
||||||
|
|
||||||
use project::{
|
use project::{
|
||||||
ProjectPath,
|
ProjectPath,
|
||||||
debugger::breakpoint_store::Breakpoint,
|
debugger::breakpoint_store::{Breakpoint, BreakpointSessionState},
|
||||||
project_settings::{GitGutterSetting, GitHunkStyleSetting, ProjectSettings},
|
project_settings::{GitGutterSetting, GitHunkStyleSetting, ProjectSettings},
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -2317,7 +2317,7 @@ impl EditorElement {
|
||||||
gutter_hitbox: &Hitbox,
|
gutter_hitbox: &Hitbox,
|
||||||
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
||||||
snapshot: &EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
breakpoints: HashMap<DisplayRow, (Anchor, Breakpoint)>,
|
breakpoints: HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)>,
|
||||||
row_infos: &[RowInfo],
|
row_infos: &[RowInfo],
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
|
@ -2325,7 +2325,7 @@ impl EditorElement {
|
||||||
self.editor.update(cx, |editor, cx| {
|
self.editor.update(cx, |editor, cx| {
|
||||||
breakpoints
|
breakpoints
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(display_row, (text_anchor, bp))| {
|
.filter_map(|(display_row, (text_anchor, bp, state))| {
|
||||||
if row_infos
|
if row_infos
|
||||||
.get((display_row.0.saturating_sub(range.start.0)) as usize)
|
.get((display_row.0.saturating_sub(range.start.0)) as usize)
|
||||||
.is_some_and(|row_info| {
|
.is_some_and(|row_info| {
|
||||||
|
@ -2348,7 +2348,7 @@ impl EditorElement {
|
||||||
return None;
|
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(
|
let button = prepaint_gutter_button(
|
||||||
button,
|
button,
|
||||||
|
@ -2378,7 +2378,7 @@ impl EditorElement {
|
||||||
gutter_hitbox: &Hitbox,
|
gutter_hitbox: &Hitbox,
|
||||||
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
display_hunks: &[(DisplayDiffHunk, Option<Hitbox>)],
|
||||||
snapshot: &EditorSnapshot,
|
snapshot: &EditorSnapshot,
|
||||||
breakpoints: &mut HashMap<DisplayRow, (Anchor, Breakpoint)>,
|
breakpoints: &mut HashMap<DisplayRow, (Anchor, Breakpoint, Option<BreakpointSessionState>)>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Vec<AnyElement> {
|
) -> Vec<AnyElement> {
|
||||||
|
@ -7437,8 +7437,10 @@ impl Element for EditorElement {
|
||||||
editor.active_breakpoints(start_row..end_row, window, cx)
|
editor.active_breakpoints(start_row..end_row, window, cx)
|
||||||
});
|
});
|
||||||
if cx.has_flag::<DebuggerFeatureFlag>() {
|
if cx.has_flag::<DebuggerFeatureFlag>() {
|
||||||
for display_row in breakpoint_rows.keys() {
|
for (display_row, (_, bp, state)) in &breakpoint_rows {
|
||||||
active_rows.entry(*display_row).or_default().breakpoint = true;
|
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();
|
let breakpoint = Breakpoint::new_standard();
|
||||||
phantom_breakpoint.collides_with_existing_breakpoint =
|
phantom_breakpoint.collides_with_existing_breakpoint =
|
||||||
false;
|
false;
|
||||||
(position, breakpoint)
|
(position, breakpoint, None)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
//!
|
//!
|
||||||
//! Breakpoints are separate from a session because they're not associated with any particular debug session. They can also be set up without a session running.
|
//! Breakpoints are separate from a session because they're not associated with any particular debug session. They can also be set up without a session running.
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{Result, anyhow};
|
||||||
use breakpoints_in_file::BreakpointsInFile;
|
pub use breakpoints_in_file::{BreakpointSessionState, BreakpointWithPosition};
|
||||||
use collections::BTreeMap;
|
use breakpoints_in_file::{BreakpointsInFile, StatefulBreakpoint};
|
||||||
|
use collections::{BTreeMap, HashMap};
|
||||||
use dap::{StackFrameId, client::SessionId};
|
use dap::{StackFrameId, client::SessionId};
|
||||||
use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, Subscription, Task};
|
use gpui::{App, AppContext, AsyncApp, Context, Entity, EventEmitter, Subscription, Task};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -14,21 +15,54 @@ use rpc::{
|
||||||
};
|
};
|
||||||
use std::{hash::Hash, ops::Range, path::Path, sync::Arc, u32};
|
use std::{hash::Hash, ops::Range, path::Path, sync::Arc, u32};
|
||||||
use text::{Point, PointUtf16};
|
use text::{Point, PointUtf16};
|
||||||
|
use util::maybe;
|
||||||
|
|
||||||
use crate::{Project, ProjectPath, buffer_store::BufferStore, worktree_store::WorktreeStore};
|
use crate::{Project, ProjectPath, buffer_store::BufferStore, worktree_store::WorktreeStore};
|
||||||
|
|
||||||
use super::session::ThreadId;
|
use super::session::ThreadId;
|
||||||
|
|
||||||
mod breakpoints_in_file {
|
mod breakpoints_in_file {
|
||||||
|
use collections::HashMap;
|
||||||
use language::{BufferEvent, DiskState};
|
use language::{BufferEvent, DiskState};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct BreakpointWithPosition {
|
||||||
|
pub position: text::Anchor,
|
||||||
|
pub bp: Breakpoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A breakpoint with per-session data about it's state (as seen by the Debug Adapter).
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct StatefulBreakpoint {
|
||||||
|
pub bp: BreakpointWithPosition,
|
||||||
|
pub session_state: HashMap<SessionId, BreakpointSessionState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulBreakpoint {
|
||||||
|
pub(super) fn new(bp: BreakpointWithPosition) -> Self {
|
||||||
|
Self {
|
||||||
|
bp,
|
||||||
|
session_state: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub(super) fn position(&self) -> &text::Anchor {
|
||||||
|
&self.bp.position
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||||
|
pub struct BreakpointSessionState {
|
||||||
|
/// Session-specific identifier for the breakpoint, as assigned by Debug Adapter.
|
||||||
|
pub id: u64,
|
||||||
|
pub verified: bool,
|
||||||
|
}
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct BreakpointsInFile {
|
pub(super) struct BreakpointsInFile {
|
||||||
pub(super) buffer: Entity<Buffer>,
|
pub(super) buffer: Entity<Buffer>,
|
||||||
// TODO: This is.. less than ideal, as it's O(n) and does not return entries in order. We'll have to change TreeMap to support passing in the context for comparisons
|
// TODO: This is.. less than ideal, as it's O(n) and does not return entries in order. We'll have to change TreeMap to support passing in the context for comparisons
|
||||||
pub(super) breakpoints: Vec<(text::Anchor, Breakpoint)>,
|
pub(super) breakpoints: Vec<StatefulBreakpoint>,
|
||||||
_subscription: Arc<Subscription>,
|
_subscription: Arc<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,9 +233,26 @@ impl BreakpointStore {
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|breakpoint| {
|
.filter_map(|breakpoint| {
|
||||||
let anchor = language::proto::deserialize_anchor(breakpoint.position.clone()?)?;
|
let position =
|
||||||
|
language::proto::deserialize_anchor(breakpoint.position.clone()?)?;
|
||||||
|
let session_state = breakpoint
|
||||||
|
.session_state
|
||||||
|
.iter()
|
||||||
|
.map(|(session_id, state)| {
|
||||||
|
let state = BreakpointSessionState {
|
||||||
|
id: state.id,
|
||||||
|
verified: state.verified,
|
||||||
|
};
|
||||||
|
(SessionId::from_proto(*session_id), state)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
let breakpoint = Breakpoint::from_proto(breakpoint)?;
|
let breakpoint = Breakpoint::from_proto(breakpoint)?;
|
||||||
Some((anchor, breakpoint))
|
let bp = BreakpointWithPosition {
|
||||||
|
position,
|
||||||
|
bp: breakpoint,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(StatefulBreakpoint { bp, session_state })
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -231,7 +282,7 @@ impl BreakpointStore {
|
||||||
.payload
|
.payload
|
||||||
.breakpoint
|
.breakpoint
|
||||||
.ok_or_else(|| anyhow!("Breakpoint not present in RPC payload"))?;
|
.ok_or_else(|| anyhow!("Breakpoint not present in RPC payload"))?;
|
||||||
let anchor = language::proto::deserialize_anchor(
|
let position = language::proto::deserialize_anchor(
|
||||||
breakpoint
|
breakpoint
|
||||||
.position
|
.position
|
||||||
.clone()
|
.clone()
|
||||||
|
@ -244,7 +295,10 @@ impl BreakpointStore {
|
||||||
breakpoints.update(&mut cx, |this, cx| {
|
breakpoints.update(&mut cx, |this, cx| {
|
||||||
this.toggle_breakpoint(
|
this.toggle_breakpoint(
|
||||||
buffer,
|
buffer,
|
||||||
(anchor, breakpoint),
|
BreakpointWithPosition {
|
||||||
|
position,
|
||||||
|
bp: breakpoint,
|
||||||
|
},
|
||||||
BreakpointEditAction::Toggle,
|
BreakpointEditAction::Toggle,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -261,13 +315,76 @@ impl BreakpointStore {
|
||||||
breakpoints: breakpoint_set
|
breakpoints: breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(anchor, bp)| bp.to_proto(&path, anchor))
|
.filter_map(|breakpoint| {
|
||||||
|
breakpoint.bp.bp.to_proto(
|
||||||
|
&path,
|
||||||
|
&breakpoint.position(),
|
||||||
|
&breakpoint.session_state,
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn update_session_breakpoint(
|
||||||
|
&mut self,
|
||||||
|
session_id: SessionId,
|
||||||
|
_: dap::BreakpointEventReason,
|
||||||
|
breakpoint: dap::Breakpoint,
|
||||||
|
) {
|
||||||
|
maybe!({
|
||||||
|
let event_id = breakpoint.id?;
|
||||||
|
|
||||||
|
let state = self
|
||||||
|
.breakpoints
|
||||||
|
.values_mut()
|
||||||
|
.find_map(|breakpoints_in_file| {
|
||||||
|
breakpoints_in_file
|
||||||
|
.breakpoints
|
||||||
|
.iter_mut()
|
||||||
|
.find_map(|state| {
|
||||||
|
let state = state.session_state.get_mut(&session_id)?;
|
||||||
|
|
||||||
|
if state.id == event_id {
|
||||||
|
Some(state)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
state.verified = breakpoint.verified;
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn mark_breakpoints_verified(
|
||||||
|
&mut self,
|
||||||
|
session_id: SessionId,
|
||||||
|
abs_path: &Path,
|
||||||
|
|
||||||
|
it: impl Iterator<Item = (BreakpointWithPosition, BreakpointSessionState)>,
|
||||||
|
) {
|
||||||
|
maybe!({
|
||||||
|
let breakpoints = self.breakpoints.get_mut(abs_path)?;
|
||||||
|
for (breakpoint, state) in it {
|
||||||
|
if let Some(to_update) = breakpoints
|
||||||
|
.breakpoints
|
||||||
|
.iter_mut()
|
||||||
|
.find(|bp| *bp.position() == breakpoint.position)
|
||||||
|
{
|
||||||
|
to_update
|
||||||
|
.session_state
|
||||||
|
.entry(session_id)
|
||||||
|
.insert_entry(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn abs_path_from_buffer(buffer: &Entity<Buffer>, cx: &App) -> Option<Arc<Path>> {
|
pub fn abs_path_from_buffer(buffer: &Entity<Buffer>, cx: &App) -> Option<Arc<Path>> {
|
||||||
worktree::File::from_dyn(buffer.read(cx).file())
|
worktree::File::from_dyn(buffer.read(cx).file())
|
||||||
.and_then(|file| file.worktree.read(cx).absolutize(&file.path).ok())
|
.and_then(|file| file.worktree.read(cx).absolutize(&file.path).ok())
|
||||||
|
@ -277,7 +394,7 @@ impl BreakpointStore {
|
||||||
pub fn toggle_breakpoint(
|
pub fn toggle_breakpoint(
|
||||||
&mut self,
|
&mut self,
|
||||||
buffer: Entity<Buffer>,
|
buffer: Entity<Buffer>,
|
||||||
mut breakpoint: (text::Anchor, Breakpoint),
|
mut breakpoint: BreakpointWithPosition,
|
||||||
edit_action: BreakpointEditAction,
|
edit_action: BreakpointEditAction,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
|
@ -295,54 +412,57 @@ impl BreakpointStore {
|
||||||
let len_before = breakpoint_set.breakpoints.len();
|
let len_before = breakpoint_set.breakpoints.len();
|
||||||
breakpoint_set
|
breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.retain(|value| &breakpoint != value);
|
.retain(|value| breakpoint != value.bp);
|
||||||
if len_before == breakpoint_set.breakpoints.len() {
|
if len_before == breakpoint_set.breakpoints.len() {
|
||||||
// We did not remove any breakpoint, hence let's toggle one.
|
// We did not remove any breakpoint, hence let's toggle one.
|
||||||
breakpoint_set.breakpoints.push(breakpoint.clone());
|
breakpoint_set
|
||||||
|
.breakpoints
|
||||||
|
.push(StatefulBreakpoint::new(breakpoint.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BreakpointEditAction::InvertState => {
|
BreakpointEditAction::InvertState => {
|
||||||
if let Some((_, bp)) = breakpoint_set
|
if let Some(bp) = breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|value| breakpoint == **value)
|
.find(|value| breakpoint == value.bp)
|
||||||
{
|
{
|
||||||
|
let bp = &mut bp.bp.bp;
|
||||||
if bp.is_enabled() {
|
if bp.is_enabled() {
|
||||||
bp.state = BreakpointState::Disabled;
|
bp.state = BreakpointState::Disabled;
|
||||||
} else {
|
} else {
|
||||||
bp.state = BreakpointState::Enabled;
|
bp.state = BreakpointState::Enabled;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
breakpoint.1.state = BreakpointState::Disabled;
|
breakpoint.bp.state = BreakpointState::Disabled;
|
||||||
breakpoint_set.breakpoints.push(breakpoint.clone());
|
breakpoint_set
|
||||||
|
.breakpoints
|
||||||
|
.push(StatefulBreakpoint::new(breakpoint.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BreakpointEditAction::EditLogMessage(log_message) => {
|
BreakpointEditAction::EditLogMessage(log_message) => {
|
||||||
if !log_message.is_empty() {
|
if !log_message.is_empty() {
|
||||||
let found_bp =
|
let found_bp = breakpoint_set.breakpoints.iter_mut().find_map(|bp| {
|
||||||
breakpoint_set
|
if breakpoint.position == *bp.position() {
|
||||||
.breakpoints
|
Some(&mut bp.bp.bp)
|
||||||
.iter_mut()
|
} else {
|
||||||
.find_map(|(other_pos, other_bp)| {
|
None
|
||||||
if breakpoint.0 == *other_pos {
|
}
|
||||||
Some(other_bp)
|
});
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(found_bp) = found_bp {
|
if let Some(found_bp) = found_bp {
|
||||||
found_bp.message = Some(log_message.clone());
|
found_bp.message = Some(log_message.clone());
|
||||||
} else {
|
} else {
|
||||||
breakpoint.1.message = Some(log_message.clone());
|
breakpoint.bp.message = Some(log_message.clone());
|
||||||
// We did not remove any breakpoint, hence let's toggle one.
|
// We did not remove any breakpoint, hence let's toggle one.
|
||||||
breakpoint_set.breakpoints.push(breakpoint.clone());
|
breakpoint_set
|
||||||
|
.breakpoints
|
||||||
|
.push(StatefulBreakpoint::new(breakpoint.clone()));
|
||||||
}
|
}
|
||||||
} else if breakpoint.1.message.is_some() {
|
} else if breakpoint.bp.message.is_some() {
|
||||||
if let Some(position) = breakpoint_set
|
if let Some(position) = breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.find_position(|(pos, bp)| &breakpoint.0 == pos && bp == &breakpoint.1)
|
.find_position(|other| breakpoint == other.bp)
|
||||||
.map(|res| res.0)
|
.map(|res| res.0)
|
||||||
{
|
{
|
||||||
breakpoint_set.breakpoints.remove(position);
|
breakpoint_set.breakpoints.remove(position);
|
||||||
|
@ -353,30 +473,28 @@ impl BreakpointStore {
|
||||||
}
|
}
|
||||||
BreakpointEditAction::EditHitCondition(hit_condition) => {
|
BreakpointEditAction::EditHitCondition(hit_condition) => {
|
||||||
if !hit_condition.is_empty() {
|
if !hit_condition.is_empty() {
|
||||||
let found_bp =
|
let found_bp = breakpoint_set.breakpoints.iter_mut().find_map(|other| {
|
||||||
breakpoint_set
|
if breakpoint.position == *other.position() {
|
||||||
.breakpoints
|
Some(&mut other.bp.bp)
|
||||||
.iter_mut()
|
} else {
|
||||||
.find_map(|(other_pos, other_bp)| {
|
None
|
||||||
if breakpoint.0 == *other_pos {
|
}
|
||||||
Some(other_bp)
|
});
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(found_bp) = found_bp {
|
if let Some(found_bp) = found_bp {
|
||||||
found_bp.hit_condition = Some(hit_condition.clone());
|
found_bp.hit_condition = Some(hit_condition.clone());
|
||||||
} else {
|
} else {
|
||||||
breakpoint.1.hit_condition = Some(hit_condition.clone());
|
breakpoint.bp.hit_condition = Some(hit_condition.clone());
|
||||||
// We did not remove any breakpoint, hence let's toggle one.
|
// We did not remove any breakpoint, hence let's toggle one.
|
||||||
breakpoint_set.breakpoints.push(breakpoint.clone());
|
breakpoint_set
|
||||||
|
.breakpoints
|
||||||
|
.push(StatefulBreakpoint::new(breakpoint.clone()))
|
||||||
}
|
}
|
||||||
} else if breakpoint.1.hit_condition.is_some() {
|
} else if breakpoint.bp.hit_condition.is_some() {
|
||||||
if let Some(position) = breakpoint_set
|
if let Some(position) = breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.find_position(|(pos, bp)| &breakpoint.0 == pos && bp == &breakpoint.1)
|
.find_position(|bp| breakpoint == bp.bp)
|
||||||
.map(|res| res.0)
|
.map(|res| res.0)
|
||||||
{
|
{
|
||||||
breakpoint_set.breakpoints.remove(position);
|
breakpoint_set.breakpoints.remove(position);
|
||||||
|
@ -387,30 +505,28 @@ impl BreakpointStore {
|
||||||
}
|
}
|
||||||
BreakpointEditAction::EditCondition(condition) => {
|
BreakpointEditAction::EditCondition(condition) => {
|
||||||
if !condition.is_empty() {
|
if !condition.is_empty() {
|
||||||
let found_bp =
|
let found_bp = breakpoint_set.breakpoints.iter_mut().find_map(|other| {
|
||||||
breakpoint_set
|
if breakpoint.position == *other.position() {
|
||||||
.breakpoints
|
Some(&mut other.bp.bp)
|
||||||
.iter_mut()
|
} else {
|
||||||
.find_map(|(other_pos, other_bp)| {
|
None
|
||||||
if breakpoint.0 == *other_pos {
|
}
|
||||||
Some(other_bp)
|
});
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(found_bp) = found_bp {
|
if let Some(found_bp) = found_bp {
|
||||||
found_bp.condition = Some(condition.clone());
|
found_bp.condition = Some(condition.clone());
|
||||||
} else {
|
} else {
|
||||||
breakpoint.1.condition = Some(condition.clone());
|
breakpoint.bp.condition = Some(condition.clone());
|
||||||
// We did not remove any breakpoint, hence let's toggle one.
|
// We did not remove any breakpoint, hence let's toggle one.
|
||||||
breakpoint_set.breakpoints.push(breakpoint.clone());
|
breakpoint_set
|
||||||
|
.breakpoints
|
||||||
|
.push(StatefulBreakpoint::new(breakpoint.clone()));
|
||||||
}
|
}
|
||||||
} else if breakpoint.1.condition.is_some() {
|
} else if breakpoint.bp.condition.is_some() {
|
||||||
if let Some(position) = breakpoint_set
|
if let Some(position) = breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.find_position(|(pos, bp)| &breakpoint.0 == pos && bp == &breakpoint.1)
|
.find_position(|bp| breakpoint == bp.bp)
|
||||||
.map(|res| res.0)
|
.map(|res| res.0)
|
||||||
{
|
{
|
||||||
breakpoint_set.breakpoints.remove(position);
|
breakpoint_set.breakpoints.remove(position);
|
||||||
|
@ -425,7 +541,11 @@ impl BreakpointStore {
|
||||||
self.breakpoints.remove(&abs_path);
|
self.breakpoints.remove(&abs_path);
|
||||||
}
|
}
|
||||||
if let BreakpointStoreMode::Remote(remote) = &self.mode {
|
if let BreakpointStoreMode::Remote(remote) = &self.mode {
|
||||||
if let Some(breakpoint) = breakpoint.1.to_proto(&abs_path, &breakpoint.0) {
|
if let Some(breakpoint) =
|
||||||
|
breakpoint
|
||||||
|
.bp
|
||||||
|
.to_proto(&abs_path, &breakpoint.position, &HashMap::default())
|
||||||
|
{
|
||||||
cx.background_spawn(remote.upstream_client.request(proto::ToggleBreakpoint {
|
cx.background_spawn(remote.upstream_client.request(proto::ToggleBreakpoint {
|
||||||
project_id: remote._upstream_project_id,
|
project_id: remote._upstream_project_id,
|
||||||
path: abs_path.to_str().map(ToOwned::to_owned).unwrap(),
|
path: abs_path.to_str().map(ToOwned::to_owned).unwrap(),
|
||||||
|
@ -441,7 +561,11 @@ impl BreakpointStore {
|
||||||
breakpoint_set
|
breakpoint_set
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(anchor, bp)| bp.to_proto(&abs_path, anchor))
|
.filter_map(|bp| {
|
||||||
|
bp.bp
|
||||||
|
.bp
|
||||||
|
.to_proto(&abs_path, bp.position(), &bp.session_state)
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
@ -485,21 +609,31 @@ impl BreakpointStore {
|
||||||
range: Option<Range<text::Anchor>>,
|
range: Option<Range<text::Anchor>>,
|
||||||
buffer_snapshot: &'a BufferSnapshot,
|
buffer_snapshot: &'a BufferSnapshot,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> impl Iterator<Item = &'a (text::Anchor, Breakpoint)> + 'a {
|
) -> impl Iterator<Item = (&'a BreakpointWithPosition, Option<BreakpointSessionState>)> + 'a
|
||||||
|
{
|
||||||
let abs_path = Self::abs_path_from_buffer(buffer, cx);
|
let abs_path = Self::abs_path_from_buffer(buffer, cx);
|
||||||
|
let active_session_id = self
|
||||||
|
.active_stack_frame
|
||||||
|
.as_ref()
|
||||||
|
.map(|frame| frame.session_id);
|
||||||
abs_path
|
abs_path
|
||||||
.and_then(|path| self.breakpoints.get(&path))
|
.and_then(|path| self.breakpoints.get(&path))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(move |file_breakpoints| {
|
.flat_map(move |file_breakpoints| {
|
||||||
file_breakpoints.breakpoints.iter().filter({
|
file_breakpoints.breakpoints.iter().filter_map({
|
||||||
let range = range.clone();
|
let range = range.clone();
|
||||||
move |(position, _)| {
|
move |bp| {
|
||||||
if let Some(range) = &range {
|
if let Some(range) = &range {
|
||||||
position.cmp(&range.start, buffer_snapshot).is_ge()
|
if bp.position().cmp(&range.start, buffer_snapshot).is_lt()
|
||||||
&& position.cmp(&range.end, buffer_snapshot).is_le()
|
|| bp.position().cmp(&range.end, buffer_snapshot).is_gt()
|
||||||
} else {
|
{
|
||||||
true
|
return None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let session_state = active_session_id
|
||||||
|
.and_then(|id| bp.session_state.get(&id))
|
||||||
|
.copied();
|
||||||
|
Some((&bp.bp, session_state))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -549,34 +683,46 @@ impl BreakpointStore {
|
||||||
path: &Path,
|
path: &Path,
|
||||||
row: u32,
|
row: u32,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Option<(Entity<Buffer>, (text::Anchor, Breakpoint))> {
|
) -> Option<(Entity<Buffer>, BreakpointWithPosition)> {
|
||||||
self.breakpoints.get(path).and_then(|breakpoints| {
|
self.breakpoints.get(path).and_then(|breakpoints| {
|
||||||
let snapshot = breakpoints.buffer.read(cx).text_snapshot();
|
let snapshot = breakpoints.buffer.read(cx).text_snapshot();
|
||||||
|
|
||||||
breakpoints
|
breakpoints
|
||||||
.breakpoints
|
.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(anchor, _)| anchor.summary::<Point>(&snapshot).row == row)
|
.find(|bp| bp.position().summary::<Point>(&snapshot).row == row)
|
||||||
.map(|breakpoint| (breakpoints.buffer.clone(), breakpoint.clone()))
|
.map(|breakpoint| (breakpoints.buffer.clone(), breakpoint.bp.clone()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn breakpoints_from_path(&self, path: &Arc<Path>, cx: &App) -> Vec<SourceBreakpoint> {
|
pub fn breakpoints_from_path(&self, path: &Arc<Path>) -> Vec<BreakpointWithPosition> {
|
||||||
|
self.breakpoints
|
||||||
|
.get(path)
|
||||||
|
.map(|bp| bp.breakpoints.iter().map(|bp| bp.bp.clone()).collect())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source_breakpoints_from_path(
|
||||||
|
&self,
|
||||||
|
path: &Arc<Path>,
|
||||||
|
cx: &App,
|
||||||
|
) -> Vec<SourceBreakpoint> {
|
||||||
self.breakpoints
|
self.breakpoints
|
||||||
.get(path)
|
.get(path)
|
||||||
.map(|bp| {
|
.map(|bp| {
|
||||||
let snapshot = bp.buffer.read(cx).snapshot();
|
let snapshot = bp.buffer.read(cx).snapshot();
|
||||||
bp.breakpoints
|
bp.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(position, breakpoint)| {
|
.map(|bp| {
|
||||||
let position = snapshot.summary_for_anchor::<PointUtf16>(position).row;
|
let position = snapshot.summary_for_anchor::<PointUtf16>(bp.position()).row;
|
||||||
|
let bp = &bp.bp;
|
||||||
SourceBreakpoint {
|
SourceBreakpoint {
|
||||||
row: position,
|
row: position,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
state: breakpoint.state,
|
state: bp.bp.state,
|
||||||
message: breakpoint.message.clone(),
|
message: bp.bp.message.clone(),
|
||||||
condition: breakpoint.condition.clone(),
|
condition: bp.bp.condition.clone(),
|
||||||
hit_condition: breakpoint.hit_condition.clone(),
|
hit_condition: bp.bp.hit_condition.clone(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -584,7 +730,18 @@ impl BreakpointStore {
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_breakpoints(&self, cx: &App) -> BTreeMap<Arc<Path>, Vec<SourceBreakpoint>> {
|
pub fn all_breakpoints(&self) -> BTreeMap<Arc<Path>, Vec<BreakpointWithPosition>> {
|
||||||
|
self.breakpoints
|
||||||
|
.iter()
|
||||||
|
.map(|(path, bp)| {
|
||||||
|
(
|
||||||
|
path.clone(),
|
||||||
|
bp.breakpoints.iter().map(|bp| bp.bp.clone()).collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
pub fn all_source_breakpoints(&self, cx: &App) -> BTreeMap<Arc<Path>, Vec<SourceBreakpoint>> {
|
||||||
self.breakpoints
|
self.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(path, bp)| {
|
.map(|(path, bp)| {
|
||||||
|
@ -593,15 +750,18 @@ impl BreakpointStore {
|
||||||
path.clone(),
|
path.clone(),
|
||||||
bp.breakpoints
|
bp.breakpoints
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(position, breakpoint)| {
|
.map(|breakpoint| {
|
||||||
let position = snapshot.summary_for_anchor::<PointUtf16>(position).row;
|
let position = snapshot
|
||||||
|
.summary_for_anchor::<PointUtf16>(&breakpoint.position())
|
||||||
|
.row;
|
||||||
|
let breakpoint = &breakpoint.bp;
|
||||||
SourceBreakpoint {
|
SourceBreakpoint {
|
||||||
row: position,
|
row: position,
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
message: breakpoint.message.clone(),
|
message: breakpoint.bp.message.clone(),
|
||||||
state: breakpoint.state,
|
state: breakpoint.bp.state,
|
||||||
hit_condition: breakpoint.hit_condition.clone(),
|
hit_condition: breakpoint.bp.hit_condition.clone(),
|
||||||
condition: breakpoint.condition.clone(),
|
condition: breakpoint.bp.condition.clone(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -656,15 +816,17 @@ impl BreakpointStore {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let position = snapshot.anchor_after(point);
|
let position = snapshot.anchor_after(point);
|
||||||
breakpoints_for_file.breakpoints.push((
|
breakpoints_for_file
|
||||||
position,
|
.breakpoints
|
||||||
Breakpoint {
|
.push(StatefulBreakpoint::new(BreakpointWithPosition {
|
||||||
message: bp.message,
|
position,
|
||||||
state: bp.state,
|
bp: Breakpoint {
|
||||||
condition: bp.condition,
|
message: bp.message,
|
||||||
hit_condition: bp.hit_condition,
|
state: bp.state,
|
||||||
},
|
condition: bp.condition,
|
||||||
))
|
hit_condition: bp.hit_condition,
|
||||||
|
},
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
new_breakpoints.insert(path, breakpoints_for_file);
|
new_breakpoints.insert(path, breakpoints_for_file);
|
||||||
}
|
}
|
||||||
|
@ -755,7 +917,7 @@ impl BreakpointState {
|
||||||
pub struct Breakpoint {
|
pub struct Breakpoint {
|
||||||
pub message: Option<BreakpointMessage>,
|
pub message: Option<BreakpointMessage>,
|
||||||
/// How many times do we hit the breakpoint until we actually stop at it e.g. (2 = 2 times of the breakpoint action)
|
/// How many times do we hit the breakpoint until we actually stop at it e.g. (2 = 2 times of the breakpoint action)
|
||||||
pub hit_condition: Option<BreakpointMessage>,
|
pub hit_condition: Option<Arc<str>>,
|
||||||
pub condition: Option<BreakpointMessage>,
|
pub condition: Option<BreakpointMessage>,
|
||||||
pub state: BreakpointState,
|
pub state: BreakpointState,
|
||||||
}
|
}
|
||||||
|
@ -788,7 +950,12 @@ impl Breakpoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_proto(&self, _path: &Path, position: &text::Anchor) -> Option<client::proto::Breakpoint> {
|
fn to_proto(
|
||||||
|
&self,
|
||||||
|
_path: &Path,
|
||||||
|
position: &text::Anchor,
|
||||||
|
session_states: &HashMap<SessionId, BreakpointSessionState>,
|
||||||
|
) -> Option<client::proto::Breakpoint> {
|
||||||
Some(client::proto::Breakpoint {
|
Some(client::proto::Breakpoint {
|
||||||
position: Some(serialize_text_anchor(position)),
|
position: Some(serialize_text_anchor(position)),
|
||||||
state: match self.state {
|
state: match self.state {
|
||||||
|
@ -801,6 +968,18 @@ impl Breakpoint {
|
||||||
.hit_condition
|
.hit_condition
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|s| String::from(s.as_ref())),
|
.map(|s| String::from(s.as_ref())),
|
||||||
|
session_state: session_states
|
||||||
|
.iter()
|
||||||
|
.map(|(session_id, state)| {
|
||||||
|
(
|
||||||
|
session_id.to_proto(),
|
||||||
|
proto::BreakpointSessionState {
|
||||||
|
id: state.id,
|
||||||
|
verified: state.verified,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::debugger::breakpoint_store::BreakpointSessionState;
|
||||||
|
|
||||||
use super::breakpoint_store::{
|
use super::breakpoint_store::{
|
||||||
BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,
|
BreakpointStore, BreakpointStoreEvent, BreakpointUpdatedReason, SourceBreakpoint,
|
||||||
};
|
};
|
||||||
|
@ -218,25 +220,55 @@ impl LocalMode {
|
||||||
breakpoint_store: &Entity<BreakpointStore>,
|
breakpoint_store: &Entity<BreakpointStore>,
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Task<()> {
|
) -> Task<()> {
|
||||||
let breakpoints = breakpoint_store
|
let breakpoints =
|
||||||
.read_with(cx, |store, cx| store.breakpoints_from_path(&abs_path, cx))
|
breakpoint_store
|
||||||
|
.read_with(cx, |store, cx| {
|
||||||
|
store.source_breakpoints_from_path(&abs_path, cx)
|
||||||
|
})
|
||||||
|
.into_iter()
|
||||||
|
.filter(|bp| bp.state.is_enabled())
|
||||||
|
.chain(self.tmp_breakpoint.iter().filter_map(|breakpoint| {
|
||||||
|
breakpoint.path.eq(&abs_path).then(|| breakpoint.clone())
|
||||||
|
}))
|
||||||
|
.map(Into::into)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let raw_breakpoints = breakpoint_store
|
||||||
|
.read(cx)
|
||||||
|
.breakpoints_from_path(&abs_path)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|bp| bp.state.is_enabled())
|
.filter(|bp| bp.bp.state.is_enabled())
|
||||||
.chain(self.tmp_breakpoint.clone())
|
.collect::<Vec<_>>();
|
||||||
.map(Into::into)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let task = self.request(dap_command::SetBreakpoints {
|
let task = self.request(dap_command::SetBreakpoints {
|
||||||
source: client_source(&abs_path),
|
source: client_source(&abs_path),
|
||||||
source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)),
|
source_modified: Some(matches!(reason, BreakpointUpdatedReason::FileSaved)),
|
||||||
breakpoints,
|
breakpoints,
|
||||||
});
|
});
|
||||||
|
let session_id = self.client.id();
|
||||||
cx.background_spawn(async move {
|
let breakpoint_store = breakpoint_store.downgrade();
|
||||||
match task.await {
|
cx.spawn(async move |cx| match cx.background_spawn(task).await {
|
||||||
Ok(_) => {}
|
Ok(breakpoints) => {
|
||||||
Err(err) => log::warn!("Set breakpoints request failed for path: {}", err),
|
let breakpoints =
|
||||||
|
breakpoints
|
||||||
|
.into_iter()
|
||||||
|
.zip(raw_breakpoints)
|
||||||
|
.filter_map(|(dap_bp, zed_bp)| {
|
||||||
|
Some((
|
||||||
|
zed_bp,
|
||||||
|
BreakpointSessionState {
|
||||||
|
id: dap_bp.id?,
|
||||||
|
verified: dap_bp.verified,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
});
|
||||||
|
breakpoint_store
|
||||||
|
.update(cx, |this, _| {
|
||||||
|
this.mark_breakpoints_verified(session_id, &abs_path, breakpoints);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
Err(err) => log::warn!("Set breakpoints request failed for path: {}", err),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,8 +303,11 @@ impl LocalMode {
|
||||||
cx: &App,
|
cx: &App,
|
||||||
) -> Task<HashMap<Arc<Path>, anyhow::Error>> {
|
) -> Task<HashMap<Arc<Path>, anyhow::Error>> {
|
||||||
let mut breakpoint_tasks = Vec::new();
|
let mut breakpoint_tasks = Vec::new();
|
||||||
let breakpoints = breakpoint_store.read_with(cx, |store, cx| store.all_breakpoints(cx));
|
let breakpoints =
|
||||||
|
breakpoint_store.read_with(cx, |store, cx| store.all_source_breakpoints(cx));
|
||||||
|
let mut raw_breakpoints = breakpoint_store.read_with(cx, |this, _| this.all_breakpoints());
|
||||||
|
debug_assert_eq!(raw_breakpoints.len(), breakpoints.len());
|
||||||
|
let session_id = self.client.id();
|
||||||
for (path, breakpoints) in breakpoints {
|
for (path, breakpoints) in breakpoints {
|
||||||
let breakpoints = if ignore_breakpoints {
|
let breakpoints = if ignore_breakpoints {
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -284,14 +319,46 @@ impl LocalMode {
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
breakpoint_tasks.push(
|
let raw_breakpoints = raw_breakpoints
|
||||||
self.request(dap_command::SetBreakpoints {
|
.remove(&path)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|bp| bp.bp.state.is_enabled());
|
||||||
|
let error_path = path.clone();
|
||||||
|
let send_request = self
|
||||||
|
.request(dap_command::SetBreakpoints {
|
||||||
source: client_source(&path),
|
source: client_source(&path),
|
||||||
source_modified: Some(false),
|
source_modified: Some(false),
|
||||||
breakpoints,
|
breakpoints,
|
||||||
})
|
})
|
||||||
.map(|result| result.map_err(|e| (path, e))),
|
.map(|result| result.map_err(move |e| (error_path, e)));
|
||||||
);
|
|
||||||
|
let task = cx.spawn({
|
||||||
|
let breakpoint_store = breakpoint_store.downgrade();
|
||||||
|
async move |cx| {
|
||||||
|
let breakpoints = cx.background_spawn(send_request).await?;
|
||||||
|
|
||||||
|
let breakpoints = breakpoints.into_iter().zip(raw_breakpoints).filter_map(
|
||||||
|
|(dap_bp, zed_bp)| {
|
||||||
|
Some((
|
||||||
|
zed_bp,
|
||||||
|
BreakpointSessionState {
|
||||||
|
id: dap_bp.id?,
|
||||||
|
verified: dap_bp.verified,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
breakpoint_store
|
||||||
|
.update(cx, |this, _| {
|
||||||
|
this.mark_breakpoints_verified(session_id, &path, breakpoints);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
});
|
||||||
|
breakpoint_tasks.push(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
|
@ -1204,7 +1271,9 @@ impl Session {
|
||||||
self.output_token.0 += 1;
|
self.output_token.0 += 1;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
Events::Breakpoint(_) => {}
|
Events::Breakpoint(event) => self.breakpoint_store.update(cx, |store, _| {
|
||||||
|
store.update_session_breakpoint(self.session_id(), event.reason, event.breakpoint);
|
||||||
|
}),
|
||||||
Events::Module(event) => {
|
Events::Module(event) => {
|
||||||
match event.reason {
|
match event.reason {
|
||||||
dap::ModuleEventReason::New => {
|
dap::ModuleEventReason::New => {
|
||||||
|
|
|
@ -47,6 +47,7 @@ use dap::{DapRegistry, client::DebugAdapterClient};
|
||||||
|
|
||||||
use collections::{BTreeSet, HashMap, HashSet};
|
use collections::{BTreeSet, HashMap, HashSet};
|
||||||
use debounced_delay::DebouncedDelay;
|
use debounced_delay::DebouncedDelay;
|
||||||
|
pub use debugger::breakpoint_store::BreakpointWithPosition;
|
||||||
use debugger::{
|
use debugger::{
|
||||||
breakpoint_store::{ActiveStackFrame, BreakpointStore},
|
breakpoint_store::{ActiveStackFrame, BreakpointStore},
|
||||||
dap_store::{DapStore, DapStoreEvent},
|
dap_store::{DapStore, DapStoreEvent},
|
||||||
|
|
|
@ -3,7 +3,6 @@ fn main() {
|
||||||
build
|
build
|
||||||
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]")
|
||||||
.type_attribute("ProjectPath", "#[derive(Hash, Eq)]")
|
.type_attribute("ProjectPath", "#[derive(Hash, Eq)]")
|
||||||
.type_attribute("Breakpoint", "#[derive(Hash, Eq)]")
|
|
||||||
.type_attribute("Anchor", "#[derive(Hash, Eq)]")
|
.type_attribute("Anchor", "#[derive(Hash, Eq)]")
|
||||||
.compile_protos(&["proto/zed.proto"], &["proto"])
|
.compile_protos(&["proto/zed.proto"], &["proto"])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -16,6 +16,12 @@ message Breakpoint {
|
||||||
optional string message = 4;
|
optional string message = 4;
|
||||||
optional string condition = 5;
|
optional string condition = 5;
|
||||||
optional string hit_condition = 6;
|
optional string hit_condition = 6;
|
||||||
|
map<uint64, BreakpointSessionState> session_state = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointSessionState {
|
||||||
|
uint64 id = 1;
|
||||||
|
bool verified = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BreakpointsForFile {
|
message BreakpointsForFile {
|
||||||
|
@ -30,63 +36,6 @@ message ToggleBreakpoint {
|
||||||
Breakpoint breakpoint = 3;
|
Breakpoint breakpoint = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum DebuggerThreadItem {
|
|
||||||
Console = 0;
|
|
||||||
LoadedSource = 1;
|
|
||||||
Modules = 2;
|
|
||||||
Variables = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DebuggerSetVariableState {
|
|
||||||
string name = 1;
|
|
||||||
DapScope scope = 2;
|
|
||||||
string value = 3;
|
|
||||||
uint64 stack_frame_id = 4;
|
|
||||||
optional string evaluate_name = 5;
|
|
||||||
uint64 parent_variables_reference = 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
message VariableListOpenEntry {
|
|
||||||
oneof entry {
|
|
||||||
DebuggerOpenEntryScope scope = 1;
|
|
||||||
DebuggerOpenEntryVariable variable = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message DebuggerOpenEntryScope {
|
|
||||||
string name = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DebuggerOpenEntryVariable {
|
|
||||||
string scope_name = 1;
|
|
||||||
string name = 2;
|
|
||||||
uint64 depth = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message VariableListEntrySetState {
|
|
||||||
uint64 depth = 1;
|
|
||||||
DebuggerSetVariableState state = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message VariableListEntryVariable {
|
|
||||||
uint64 depth = 1;
|
|
||||||
DapScope scope = 2;
|
|
||||||
DapVariable variable = 3;
|
|
||||||
bool has_children = 4;
|
|
||||||
uint64 container_reference = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DebuggerScopeVariableIndex {
|
|
||||||
repeated uint64 fetched_ids = 1;
|
|
||||||
repeated DebuggerVariableContainer variables = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message DebuggerVariableContainer {
|
|
||||||
uint64 container_reference = 1;
|
|
||||||
DapVariable variable = 2;
|
|
||||||
uint64 depth = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum DapThreadStatus {
|
enum DapThreadStatus {
|
||||||
Running = 0;
|
Running = 0;
|
||||||
Stopped = 1;
|
Stopped = 1;
|
||||||
|
@ -94,18 +43,6 @@ enum DapThreadStatus {
|
||||||
Ended = 3;
|
Ended = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VariableListScopes {
|
|
||||||
uint64 stack_frame_id = 1;
|
|
||||||
repeated DapScope scopes = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
message VariableListVariables {
|
|
||||||
uint64 stack_frame_id = 1;
|
|
||||||
uint64 scope_id = 2;
|
|
||||||
DebuggerScopeVariableIndex variables = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum VariablesArgumentsFilter {
|
enum VariablesArgumentsFilter {
|
||||||
Indexed = 0;
|
Indexed = 0;
|
||||||
Named = 1;
|
Named = 1;
|
||||||
|
|
|
@ -4999,7 +4999,10 @@ impl Workspace {
|
||||||
|
|
||||||
if let Some(location) = self.serialize_workspace_location(cx) {
|
if let Some(location) = self.serialize_workspace_location(cx) {
|
||||||
let breakpoints = self.project.update(cx, |project, cx| {
|
let breakpoints = self.project.update(cx, |project, cx| {
|
||||||
project.breakpoint_store().read(cx).all_breakpoints(cx)
|
project
|
||||||
|
.breakpoint_store()
|
||||||
|
.read(cx)
|
||||||
|
.all_source_breakpoints(cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
let center_group = build_serialized_pane_group(&self.center.root, window, cx);
|
let center_group = build_serialized_pane_group(&self.center.root, window, cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue