Allow enabling/disabling breakpoints (#27280)
This PR adds the ability to enable/disable breakpoints. It also fixes a bug where toggling a log breakpoint from the breakpoint context menu would add a standard breakpoint on top of the log breakpoint instead of deleting it. todo: - [x] Add `BreakpointState` field Breakpoint that manages if a breakpoint is active or not - [x] Don't send disabled breakpoints to DAP servers - in progress - [x] Half the opacity of disabled breakpoints - in progress - [x] Add `BreakpointState` to database - [x] Editor test for enabling/disabling breakpoints - [ ] Integration Test to make sure we don't send disabled breakpoints to DAP servers - [x] Database test to make sure we properly serialize/deserialize BreakpointState Release Notes: - N/A --------- Co-authored-by: Piotr <piotr@zed.dev> Co-authored-by: Conrad <conrad@zed.dev> Co-authored-by: Mikayla Maki <mikayla.c.maki@gmail.com>
This commit is contained in:
parent
df583d73b9
commit
d70ac64fe4
9 changed files with 583 additions and 172 deletions
|
@ -32,7 +32,7 @@ use multi_buffer::{IndentGuide, PathKey};
|
|||
use parking_lot::Mutex;
|
||||
use pretty_assertions::{assert_eq, assert_ne};
|
||||
use project::{
|
||||
debugger::breakpoint_store::{BreakpointKind, SerializedBreakpoint},
|
||||
debugger::breakpoint_store::{BreakpointKind, BreakpointState, SerializedBreakpoint},
|
||||
project_settings::{LspSettings, ProjectSettings},
|
||||
FakeFs,
|
||||
};
|
||||
|
@ -17392,7 +17392,7 @@ async fn assert_highlighted_edits(
|
|||
fn assert_breakpoint(
|
||||
breakpoints: &BTreeMap<Arc<Path>, Vec<SerializedBreakpoint>>,
|
||||
path: &Arc<Path>,
|
||||
expected: Vec<(u32, BreakpointKind)>,
|
||||
expected: Vec<(u32, Breakpoint)>,
|
||||
) {
|
||||
if expected.len() == 0usize {
|
||||
assert!(!breakpoints.contains_key(path));
|
||||
|
@ -17401,7 +17401,15 @@ fn assert_breakpoint(
|
|||
.get(path)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|breakpoint| (breakpoint.position, breakpoint.kind.clone()))
|
||||
.map(|breakpoint| {
|
||||
(
|
||||
breakpoint.position,
|
||||
Breakpoint {
|
||||
kind: breakpoint.kind.clone(),
|
||||
state: breakpoint.state,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
breakpoint.sort_by_key(|(cached_position, _)| *cached_position);
|
||||
|
@ -17429,12 +17437,18 @@ fn add_log_breakpoint_at_cursor(
|
|||
|
||||
let kind = BreakpointKind::Log(Arc::from(log_message));
|
||||
|
||||
(breakpoint_position, Breakpoint { kind })
|
||||
(
|
||||
breakpoint_position,
|
||||
Breakpoint {
|
||||
kind,
|
||||
state: BreakpointState::Enabled,
|
||||
},
|
||||
)
|
||||
});
|
||||
|
||||
editor.edit_breakpoint_at_anchor(
|
||||
anchor,
|
||||
bp.kind,
|
||||
bp,
|
||||
BreakpointEditAction::EditLogMessage(log_message.into()),
|
||||
cx,
|
||||
);
|
||||
|
@ -17522,7 +17536,10 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
|
|||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
|
||||
vec![
|
||||
(0, Breakpoint::new_standard()),
|
||||
(3, Breakpoint::new_standard()),
|
||||
],
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
|
@ -17541,7 +17558,11 @@ async fn test_breakpoint_toggling(cx: &mut TestAppContext) {
|
|||
});
|
||||
|
||||
assert_eq!(1, breakpoints.len());
|
||||
assert_breakpoint(&breakpoints, &abs_path, vec![(3, BreakpointKind::Standard)]);
|
||||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![(3, Breakpoint::new_standard())],
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.move_to_end(&MoveToEnd, window, cx);
|
||||
|
@ -17628,7 +17649,7 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
|||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![(0, BreakpointKind::Log("hello world".into()))],
|
||||
vec![(0, Breakpoint::new_log("hello world"))],
|
||||
);
|
||||
|
||||
// Removing a log message from a log breakpoint should remove it
|
||||
|
@ -17669,7 +17690,10 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
|||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![(0, BreakpointKind::Standard), (3, BreakpointKind::Standard)],
|
||||
vec![
|
||||
(0, Breakpoint::new_standard()),
|
||||
(3, Breakpoint::new_standard()),
|
||||
],
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
|
@ -17690,8 +17714,8 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
|||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![
|
||||
(0, BreakpointKind::Standard),
|
||||
(3, BreakpointKind::Log("hello world".into())),
|
||||
(0, Breakpoint::new_standard()),
|
||||
(3, Breakpoint::new_log("hello world")),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -17713,8 +17737,167 @@ async fn test_log_breakpoint_editing(cx: &mut TestAppContext) {
|
|||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![
|
||||
(0, BreakpointKind::Standard),
|
||||
(3, BreakpointKind::Log("hello Earth !!".into())),
|
||||
(0, Breakpoint::new_standard()),
|
||||
(3, Breakpoint::new_log("hello Earth !!")),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// This also tests that Editor::breakpoint_at_cursor_head is working properly
|
||||
/// we had some issues where we wouldn't find a breakpoint at Point {row: 0, col: 0}
|
||||
/// or when breakpoints were placed out of order. This tests for a regression too
|
||||
#[gpui::test]
|
||||
async fn test_breakpoint_enabling_and_disabling(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let sample_text = "First line\nSecond line\nThird line\nFourth line".to_string();
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/a"),
|
||||
json!({
|
||||
"main.rs": sample_text,
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
|
||||
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
path!("/a"),
|
||||
json!({
|
||||
"main.rs": sample_text,
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs, [path!("/a").as_ref()], cx).await;
|
||||
let workspace = cx.add_window(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||
let cx = &mut VisualTestContext::from_window(*workspace.deref(), cx);
|
||||
let worktree_id = workspace
|
||||
.update(cx, |workspace, _window, cx| {
|
||||
workspace.project().update(cx, |project, cx| {
|
||||
project.worktrees(cx).next().unwrap().read(cx).id()
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_buffer((worktree_id, "main.rs"), cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let (editor, cx) = cx.add_window_view(|window, cx| {
|
||||
Editor::new(
|
||||
EditorMode::Full,
|
||||
MultiBuffer::build_from_buffer(buffer, cx),
|
||||
Some(project.clone()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
let project_path = editor.update(cx, |editor, cx| editor.project_path(cx).unwrap());
|
||||
let abs_path = project.read_with(cx, |project, cx| {
|
||||
project
|
||||
.absolute_path(&project_path, cx)
|
||||
.map(|path_buf| Arc::from(path_buf.to_owned()))
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
// assert we can add breakpoint on the first line
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
|
||||
editor.move_to_end(&MoveToEnd, window, cx);
|
||||
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
|
||||
editor.move_up(&MoveUp, window, cx);
|
||||
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
|
||||
});
|
||||
|
||||
let breakpoints = editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.breakpoint_store()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.all_breakpoints(cx)
|
||||
.clone()
|
||||
});
|
||||
|
||||
assert_eq!(1, breakpoints.len());
|
||||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![
|
||||
(0, Breakpoint::new_standard()),
|
||||
(2, Breakpoint::new_standard()),
|
||||
(3, Breakpoint::new_standard()),
|
||||
],
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.move_to_beginning(&MoveToBeginning, window, cx);
|
||||
editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
|
||||
editor.move_to_end(&MoveToEnd, window, cx);
|
||||
editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
|
||||
});
|
||||
|
||||
let breakpoints = editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.breakpoint_store()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.all_breakpoints(cx)
|
||||
.clone()
|
||||
});
|
||||
|
||||
let disable_breakpoint = {
|
||||
let mut bp = Breakpoint::new_standard();
|
||||
bp.state = BreakpointState::Disabled;
|
||||
bp
|
||||
};
|
||||
|
||||
assert_eq!(1, breakpoints.len());
|
||||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![
|
||||
(0, disable_breakpoint.clone()),
|
||||
(2, Breakpoint::new_standard()),
|
||||
(3, disable_breakpoint.clone()),
|
||||
],
|
||||
);
|
||||
|
||||
editor.update_in(cx, |editor, window, cx| {
|
||||
editor.move_to_beginning(&MoveToBeginning, window, cx);
|
||||
editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
|
||||
editor.move_to_end(&MoveToEnd, window, cx);
|
||||
editor.enable_breakpoint(&actions::EnableBreakpoint, window, cx);
|
||||
editor.move_up(&MoveUp, window, cx);
|
||||
editor.disable_breakpoint(&actions::DisableBreakpoint, window, cx);
|
||||
});
|
||||
|
||||
let breakpoints = editor.update(cx, |editor, cx| {
|
||||
editor
|
||||
.breakpoint_store()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.read(cx)
|
||||
.all_breakpoints(cx)
|
||||
.clone()
|
||||
});
|
||||
|
||||
assert_eq!(1, breakpoints.len());
|
||||
assert_breakpoint(
|
||||
&breakpoints,
|
||||
&abs_path,
|
||||
vec![
|
||||
(0, Breakpoint::new_standard()),
|
||||
(2, disable_breakpoint),
|
||||
(3, Breakpoint::new_standard()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue