diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs index cb49123a7b..21b3a69407 100644 --- a/crates/editor/src/code_context_menus.rs +++ b/crates/editor/src/code_context_menus.rs @@ -1,3 +1,4 @@ +use feature_flags::{Debugger, FeatureFlagAppExt as _}; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ AnyElement, BackgroundExecutor, Entity, Focusable, FontWeight, ListSizingBehavior, @@ -992,6 +993,17 @@ impl CodeActionsMenu { .iter() .skip(range.start) .take(range.end - range.start) + .filter(|action| { + if action + .as_task() + .map(|task| matches!(task.task_type(), task::TaskType::Debug(_))) + .unwrap_or(false) + { + cx.has_flag::() + } else { + true + } + }) .enumerate() .map(|(ix, action)| { let item_ix = range.start + ix; diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 1a1fe6926c..9a2620eda1 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4798,6 +4798,8 @@ impl Editor { Self::build_tasks_context(&project, &buffer, buffer_row, tasks, cx) }); + let debugger_flag = cx.has_flag::(); + Some(cx.spawn_in(window, async move |editor, cx| { let task_context = match task_context { Some(task_context) => task_context.await, @@ -4813,12 +4815,22 @@ impl Editor { )), }) }); - let spawn_straight_away = resolved_tasks + let spawn_straight_away = resolved_tasks.as_ref().map_or(false, |tasks| { + tasks + .templates + .iter() + .filter(|task| { + if matches!(task.1.task_type(), task::TaskType::Debug(_)) { + debugger_flag + } else { + true + } + }) + .count() + == 1 + }) && code_actions .as_ref() - .map_or(false, |tasks| tasks.templates.len() == 1) - && code_actions - .as_ref() - .map_or(true, |actions| actions.is_empty()); + .map_or(true, |actions| actions.is_empty()); if let Ok(task) = editor.update_in(cx, |editor, window, cx| { *editor.context_menu.borrow_mut() = Some(CodeContextMenu::CodeActions(CodeActionsMenu { @@ -6292,6 +6304,22 @@ impl Editor { "Set Log Breakpoint" }; + let condition_breakpoint_msg = + if breakpoint.as_ref().is_some_and(|bp| bp.condition.is_some()) { + "Edit Condition Breakpoint" + } else { + "Set Condition Breakpoint" + }; + + let hit_condition_breakpoint_msg = if breakpoint + .as_ref() + .is_some_and(|bp| bp.hit_condition.is_some()) + { + "Edit Hit Condition Breakpoint" + } else { + "Set Hit Condition Breakpoint" + }; + let set_breakpoint_msg = if breakpoint.as_ref().is_some() { "Unset Breakpoint" } else { @@ -6303,12 +6331,7 @@ impl Editor { BreakpointState::Disabled => Some("Enable"), }); - let breakpoint = breakpoint.unwrap_or_else(|| { - Arc::new(Breakpoint { - state: BreakpointState::Enabled, - message: None, - }) - }); + let breakpoint = breakpoint.unwrap_or_else(|| Arc::new(Breakpoint::new_standard())); ui::ContextMenu::build(window, cx, |menu, _, _cx| { menu.on_blur_subscription(Subscription::new(|| {})) @@ -6347,10 +6370,50 @@ impl Editor { .log_err(); } }) - .entry(log_breakpoint_msg, None, move |window, cx| { + .entry(log_breakpoint_msg, None, { + let breakpoint = breakpoint.clone(); + let weak_editor = weak_editor.clone(); + move |window, cx| { + weak_editor + .update(cx, |this, cx| { + this.add_edit_breakpoint_block( + anchor, + breakpoint.as_ref(), + BreakpointPromptEditAction::Log, + window, + cx, + ); + }) + .log_err(); + } + }) + .entry(condition_breakpoint_msg, None, { + let breakpoint = breakpoint.clone(); + let weak_editor = weak_editor.clone(); + move |window, cx| { + weak_editor + .update(cx, |this, cx| { + this.add_edit_breakpoint_block( + anchor, + breakpoint.as_ref(), + BreakpointPromptEditAction::Condition, + window, + cx, + ); + }) + .log_err(); + } + }) + .entry(hit_condition_breakpoint_msg, None, move |window, cx| { weak_editor .update(cx, |this, cx| { - this.add_edit_breakpoint_block(anchor, breakpoint.as_ref(), window, cx); + this.add_edit_breakpoint_block( + anchor, + breakpoint.as_ref(), + BreakpointPromptEditAction::HitCondition, + window, + cx, + ); }) .log_err(); }) @@ -8597,12 +8660,20 @@ impl Editor { &mut self, anchor: Anchor, breakpoint: &Breakpoint, + edit_action: BreakpointPromptEditAction, window: &mut Window, cx: &mut Context, ) { let weak_editor = cx.weak_entity(); let bp_prompt = cx.new(|cx| { - BreakpointPromptEditor::new(weak_editor, anchor, breakpoint.clone(), window, cx) + BreakpointPromptEditor::new( + weak_editor, + anchor, + breakpoint.clone(), + edit_action, + window, + cx, + ) }); let height = bp_prompt.update(cx, |this, cx| { @@ -8721,11 +8792,13 @@ impl Editor { Breakpoint { message: None, state: BreakpointState::Enabled, + condition: None, + hit_condition: None, }, ) }); - self.add_edit_breakpoint_block(anchor, &bp, window, cx); + self.add_edit_breakpoint_block(anchor, &bp, BreakpointPromptEditAction::Log, window, cx); } pub fn enable_breakpoint( @@ -19861,11 +19934,18 @@ impl Global for KillRing {} const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50); +enum BreakpointPromptEditAction { + Log, + Condition, + HitCondition, +} + struct BreakpointPromptEditor { pub(crate) prompt: Entity, editor: WeakEntity, breakpoint_anchor: Anchor, breakpoint: Breakpoint, + edit_action: BreakpointPromptEditAction, block_ids: HashSet, gutter_dimensions: Arc>, _subscriptions: Vec, @@ -19878,19 +19958,19 @@ impl BreakpointPromptEditor { editor: WeakEntity, breakpoint_anchor: Anchor, breakpoint: Breakpoint, + edit_action: BreakpointPromptEditAction, window: &mut Window, cx: &mut Context, ) -> Self { - let buffer = cx.new(|cx| { - Buffer::local( - breakpoint - .message - .as_ref() - .map(|msg| msg.to_string()) - .unwrap_or_default(), - cx, - ) - }); + let base_text = match edit_action { + BreakpointPromptEditAction::Log => breakpoint.message.as_ref(), + BreakpointPromptEditAction::Condition => breakpoint.condition.as_ref(), + BreakpointPromptEditAction::HitCondition => breakpoint.hit_condition.as_ref(), + } + .map(|msg| msg.to_string()) + .unwrap_or_default(); + + let buffer = cx.new(|cx| Buffer::local(base_text, cx)); let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx)); let prompt = cx.new(|cx| { @@ -19906,7 +19986,11 @@ impl BreakpointPromptEditor { prompt.set_soft_wrap_mode(language::language_settings::SoftWrap::EditorWidth, cx); prompt.set_show_cursor_when_unfocused(false, cx); prompt.set_placeholder_text( - "Message to log when breakpoint is hit. Expressions within {} are interpolated.", + match edit_action { + BreakpointPromptEditAction::Log => "Message to log when a breakpoint is hit. Expressions within {} are interpolated.", + BreakpointPromptEditAction::Condition => "Condition when a breakpoint is hit. Expressions within {} are interpolated.", + BreakpointPromptEditAction::HitCondition => "How many breakpoint hits to ignore", + }, cx, ); @@ -19918,6 +20002,7 @@ impl BreakpointPromptEditor { editor, breakpoint_anchor, breakpoint, + edit_action, gutter_dimensions: Arc::new(Mutex::new(GutterDimensions::default())), block_ids: Default::default(), _subscriptions: vec![], @@ -19930,7 +20015,7 @@ impl BreakpointPromptEditor { fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context) { if let Some(editor) = self.editor.upgrade() { - let log_message = self + let message = self .prompt .read(cx) .buffer @@ -19945,7 +20030,17 @@ impl BreakpointPromptEditor { editor.edit_breakpoint_at_anchor( self.breakpoint_anchor, self.breakpoint.clone(), - BreakpointEditAction::EditLogMessage(log_message.into()), + match self.edit_action { + BreakpointPromptEditAction::Log => { + BreakpointEditAction::EditLogMessage(message.into()) + } + BreakpointPromptEditAction::Condition => { + BreakpointEditAction::EditCondition(message.into()) + } + BreakpointPromptEditAction::HitCondition => { + BreakpointEditAction::EditHitCondition(message.into()) + } + }, cx, ); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 6dac9e26a4..7c46f5fe3d 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -17387,6 +17387,8 @@ fn assert_breakpoint( Breakpoint { message: breakpoint.message.clone(), state: breakpoint.state, + condition: breakpoint.condition.clone(), + hit_condition: breakpoint.hit_condition.clone(), }, ) }) @@ -17415,13 +17417,7 @@ fn add_log_breakpoint_at_cursor( .buffer_snapshot .anchor_before(Point::new(cursor_position.row, 0)); - ( - breakpoint_position, - Breakpoint { - message: Some(Arc::from(log_message)), - state: BreakpointState::Enabled, - }, - ) + (breakpoint_position, Breakpoint::new_log(&log_message)) }); editor.edit_breakpoint_at_anchor( diff --git a/crates/project/src/debugger/breakpoint_store.rs b/crates/project/src/debugger/breakpoint_store.rs index d3a182162e..d3f8c220a0 100644 --- a/crates/project/src/debugger/breakpoint_store.rs +++ b/crates/project/src/debugger/breakpoint_store.rs @@ -294,6 +294,60 @@ impl BreakpointStore { }) } } + BreakpointEditAction::EditHitCondition(hit_condition) => { + if !hit_condition.is_empty() { + let found_bp = + breakpoint_set + .breakpoints + .iter_mut() + .find_map(|(other_pos, other_bp)| { + if breakpoint.0 == *other_pos { + Some(other_bp) + } else { + None + } + }); + + if let Some(found_bp) = found_bp { + found_bp.hit_condition = Some(hit_condition.clone()); + } else { + breakpoint.1.hit_condition = Some(hit_condition.clone()); + // We did not remove any breakpoint, hence let's toggle one. + breakpoint_set.breakpoints.push(breakpoint.clone()); + } + } else if breakpoint.1.hit_condition.is_some() { + breakpoint_set.breakpoints.retain(|(other_pos, other)| { + &breakpoint.0 != other_pos && other.hit_condition.is_none() + }) + } + } + BreakpointEditAction::EditCondition(condition) => { + if !condition.is_empty() { + let found_bp = + breakpoint_set + .breakpoints + .iter_mut() + .find_map(|(other_pos, other_bp)| { + if breakpoint.0 == *other_pos { + Some(other_bp) + } else { + None + } + }); + + if let Some(found_bp) = found_bp { + found_bp.condition = Some(condition.clone()); + } else { + breakpoint.1.condition = Some(condition.clone()); + // We did not remove any breakpoint, hence let's toggle one. + breakpoint_set.breakpoints.push(breakpoint.clone()); + } + } else if breakpoint.1.condition.is_some() { + breakpoint_set.breakpoints.retain(|(other_pos, other)| { + &breakpoint.0 != other_pos && other.condition.is_none() + }) + } + } } if breakpoint_set.breakpoints.is_empty() { @@ -424,6 +478,8 @@ impl BreakpointStore { path: path.clone(), state: breakpoint.state, message: breakpoint.message.clone(), + condition: breakpoint.condition.clone(), + hit_condition: breakpoint.hit_condition.clone(), } }) .collect() @@ -447,6 +503,8 @@ impl BreakpointStore { path: path.clone(), message: breakpoint.message.clone(), state: breakpoint.state, + hit_condition: breakpoint.hit_condition.clone(), + condition: breakpoint.condition.clone(), } }) .collect(), @@ -500,6 +558,8 @@ impl BreakpointStore { Breakpoint { message: bp.message, state: bp.state, + condition: bp.condition, + hit_condition: bp.hit_condition, }, )) } @@ -537,13 +597,15 @@ pub enum BreakpointStoreEvent { impl EventEmitter for BreakpointStore {} -type LogMessage = Arc; +type BreakpointMessage = Arc; #[derive(Clone, Debug)] pub enum BreakpointEditAction { Toggle, InvertState, - EditLogMessage(LogMessage), + EditLogMessage(BreakpointMessage), + EditCondition(BreakpointMessage), + EditHitCondition(BreakpointMessage), } #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] @@ -574,7 +636,10 @@ impl BreakpointState { #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Breakpoint { - pub message: Option>, + pub message: Option, + /// 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, + pub condition: Option, pub state: BreakpointState, } @@ -582,6 +647,17 @@ impl Breakpoint { pub fn new_standard() -> Self { Self { state: BreakpointState::Enabled, + hit_condition: None, + condition: None, + message: None, + } + } + + pub fn new_condition(hit_condition: &str) -> Self { + Self { + state: BreakpointState::Enabled, + condition: None, + hit_condition: Some(hit_condition.into()), message: None, } } @@ -589,6 +665,8 @@ impl Breakpoint { pub fn new_log(log_message: &str) -> Self { Self { state: BreakpointState::Enabled, + hit_condition: None, + condition: None, message: Some(log_message.into()), } } @@ -601,6 +679,11 @@ impl Breakpoint { BreakpointState::Disabled => proto::BreakpointState::Disabled.into(), }, message: self.message.as_ref().map(|s| String::from(s.as_ref())), + condition: self.condition.as_ref().map(|s| String::from(s.as_ref())), + hit_condition: self + .hit_condition + .as_ref() + .map(|s| String::from(s.as_ref())), }) } @@ -610,7 +693,9 @@ impl Breakpoint { Some(proto::BreakpointState::Disabled) => BreakpointState::Disabled, None | Some(proto::BreakpointState::Enabled) => BreakpointState::Enabled, }, - message: breakpoint.message.map(|message| message.into()), + message: breakpoint.message.map(Into::into), + condition: breakpoint.condition.map(Into::into), + hit_condition: breakpoint.hit_condition.map(Into::into), }) } @@ -631,6 +716,8 @@ pub struct SourceBreakpoint { pub row: u32, pub path: Arc, pub message: Option>, + pub condition: Option>, + pub hit_condition: Option>, pub state: BreakpointState, } @@ -639,8 +726,12 @@ impl From for dap::SourceBreakpoint { Self { line: bp.row as u64 + 1, column: None, - condition: None, - hit_condition: None, + condition: bp + .condition + .map(|condition| String::from(condition.as_ref())), + hit_condition: bp + .hit_condition + .map(|hit_condition| String::from(hit_condition.as_ref())), log_message: bp.message.map(|message| String::from(message.as_ref())), mode: None, } diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 668344f5df..07787c0e8d 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -2673,6 +2673,8 @@ message Breakpoint { BreakpointState state = 2; reserved 3; optional string message = 4; + optional string condition = 5; + optional string hit_condition = 6; } message BreakpointsForFile { diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index 75ec3b528e..656aae9cdc 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -148,6 +148,8 @@ impl Column for SerializedWindowBounds { pub struct Breakpoint { pub position: u32, pub message: Option>, + pub condition: Option>, + pub hit_condition: Option>, pub state: BreakpointState, } @@ -190,7 +192,8 @@ struct Breakpoints(Vec); impl sqlez::bindable::StaticColumnCount for Breakpoint { fn column_count() -> usize { - 2 + BreakpointStateWrapper::column_count() + // Position, log message, condition message, and hit condition message + 4 + BreakpointStateWrapper::column_count() } } @@ -202,6 +205,8 @@ impl sqlez::bindable::Bind for Breakpoint { ) -> anyhow::Result { let next_index = statement.bind(&self.position, start_index)?; let next_index = statement.bind(&self.message, next_index)?; + let next_index = statement.bind(&self.condition, next_index)?; + let next_index = statement.bind(&self.hit_condition, next_index)?; statement.bind( &BreakpointStateWrapper(Cow::Borrowed(&self.state)), next_index, @@ -216,12 +221,16 @@ impl Column for Breakpoint { .with_context(|| format!("Failed to read BreakPoint at index {start_index}"))? as u32; let (message, next_index) = Option::::column(statement, start_index + 1)?; + let (condition, next_index) = Option::::column(statement, next_index)?; + let (hit_condition, next_index) = Option::::column(statement, next_index)?; let (state, next_index) = BreakpointStateWrapper::column(statement, next_index)?; Ok(( Breakpoint { position, message: message.map(Arc::from), + condition: condition.map(Arc::from), + hit_condition: hit_condition.map(Arc::from), state: state.0.into_owned(), }, next_index, @@ -527,7 +536,11 @@ define_connection! { sql!( ALTER TABLE breakpoints DROP COLUMN kind ), - sql!(ALTER TABLE toolchains ADD COLUMN relative_worktree_path TEXT DEFAULT "" NOT NULL) + sql!(ALTER TABLE toolchains ADD COLUMN relative_worktree_path TEXT DEFAULT "" NOT NULL), + sql!( + ALTER TABLE breakpoints ADD COLUMN condition TEXT; + ALTER TABLE breakpoints ADD COLUMN hit_condition TEXT; + ), ]; } @@ -680,7 +693,7 @@ impl WorkspaceDb { fn breakpoints(&self, workspace_id: WorkspaceId) -> BTreeMap, Vec> { let breakpoints: Result> = self .select_bound(sql! { - SELECT path, breakpoint_location, log_message, state + SELECT path, breakpoint_location, log_message, condition, hit_condition, state FROM breakpoints WHERE workspace_id = ? }) @@ -700,10 +713,20 @@ impl WorkspaceDb { row: breakpoint.position, path, message: breakpoint.message, + condition: breakpoint.condition, + hit_condition: breakpoint.hit_condition, state: breakpoint.state, }); } + for (path, bps) in map.iter() { + log::debug!( + "Got {} breakpoints from path: {}", + bps.len(), + path.to_string_lossy() + ); + } + map } Err(msg) => { @@ -727,20 +750,23 @@ impl WorkspaceDb { conn.exec_bound(sql!(DELETE FROM breakpoints WHERE workspace_id = ?1 AND path = ?2))?((workspace.id, path.as_ref())) .context("Clearing old breakpoints")?; for bp in breakpoints { - let message = bp.message; let state = BreakpointStateWrapper::from(bp.state); match conn.exec_bound(sql!( - INSERT INTO breakpoints (workspace_id, path, breakpoint_location, log_message, state) - VALUES (?1, ?2, ?3, ?4, ?5);))? + INSERT INTO breakpoints (workspace_id, path, breakpoint_location, log_message, condition, hit_condition, state) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7);))? (( workspace.id, path.as_ref(), bp.row, - message, + bp.message, + bp.condition, + bp.hit_condition, state, )) { - Ok(_) => {} + Ok(_) => { + log::debug!("Stored breakpoint at row: {} in path: {}", bp.row, path.to_string_lossy()) + } Err(err) => { log::error!("{err}"); continue; @@ -1409,18 +1435,40 @@ mod tests { position: 123, message: None, state: BreakpointState::Enabled, + condition: None, + hit_condition: None, }; let log_breakpoint = Breakpoint { position: 456, message: Some("Test log message".into()), state: BreakpointState::Enabled, + condition: None, + hit_condition: None, }; let disable_breakpoint = Breakpoint { position: 578, message: None, state: BreakpointState::Disabled, + condition: None, + hit_condition: None, + }; + + let condition_breakpoint = Breakpoint { + position: 789, + message: None, + state: BreakpointState::Enabled, + condition: Some("x > 5".into()), + hit_condition: None, + }; + + let hit_condition_breakpoint = Breakpoint { + position: 999, + message: None, + state: BreakpointState::Enabled, + condition: None, + hit_condition: Some(">= 3".into()), }; let workspace = SerializedWorkspace { @@ -1441,18 +1489,40 @@ mod tests { path: Arc::from(path), message: breakpoint.message.clone(), state: breakpoint.state, + condition: breakpoint.condition.clone(), + hit_condition: breakpoint.hit_condition.clone(), }, SourceBreakpoint { row: log_breakpoint.position, path: Arc::from(path), message: log_breakpoint.message.clone(), state: log_breakpoint.state, + condition: log_breakpoint.condition.clone(), + hit_condition: log_breakpoint.hit_condition.clone(), }, SourceBreakpoint { row: disable_breakpoint.position, path: Arc::from(path), message: disable_breakpoint.message.clone(), state: disable_breakpoint.state, + condition: disable_breakpoint.condition.clone(), + hit_condition: disable_breakpoint.hit_condition.clone(), + }, + SourceBreakpoint { + row: condition_breakpoint.position, + path: Arc::from(path), + message: condition_breakpoint.message.clone(), + state: condition_breakpoint.state, + condition: condition_breakpoint.condition.clone(), + hit_condition: condition_breakpoint.hit_condition.clone(), + }, + SourceBreakpoint { + row: hit_condition_breakpoint.position, + path: Arc::from(path), + message: hit_condition_breakpoint.message.clone(), + state: hit_condition_breakpoint.state, + condition: hit_condition_breakpoint.condition.clone(), + hit_condition: hit_condition_breakpoint.hit_condition.clone(), }, ], ); @@ -1467,22 +1537,74 @@ mod tests { let loaded = db.workspace_for_roots(&["/tmp"]).unwrap(); let loaded_breakpoints = loaded.breakpoints.get(&Arc::from(path)).unwrap(); - assert_eq!(loaded_breakpoints.len(), 3); + assert_eq!(loaded_breakpoints.len(), 5); + // normal breakpoint assert_eq!(loaded_breakpoints[0].row, breakpoint.position); assert_eq!(loaded_breakpoints[0].message, breakpoint.message); + assert_eq!(loaded_breakpoints[0].condition, breakpoint.condition); + assert_eq!( + loaded_breakpoints[0].hit_condition, + breakpoint.hit_condition + ); assert_eq!(loaded_breakpoints[0].state, breakpoint.state); assert_eq!(loaded_breakpoints[0].path, Arc::from(path)); + // enabled breakpoint assert_eq!(loaded_breakpoints[1].row, log_breakpoint.position); assert_eq!(loaded_breakpoints[1].message, log_breakpoint.message); + assert_eq!(loaded_breakpoints[1].condition, log_breakpoint.condition); + assert_eq!( + loaded_breakpoints[1].hit_condition, + log_breakpoint.hit_condition + ); assert_eq!(loaded_breakpoints[1].state, log_breakpoint.state); assert_eq!(loaded_breakpoints[1].path, Arc::from(path)); + // disable breakpoint assert_eq!(loaded_breakpoints[2].row, disable_breakpoint.position); assert_eq!(loaded_breakpoints[2].message, disable_breakpoint.message); + assert_eq!( + loaded_breakpoints[2].condition, + disable_breakpoint.condition + ); + assert_eq!( + loaded_breakpoints[2].hit_condition, + disable_breakpoint.hit_condition + ); assert_eq!(loaded_breakpoints[2].state, disable_breakpoint.state); assert_eq!(loaded_breakpoints[2].path, Arc::from(path)); + + // condition breakpoint + assert_eq!(loaded_breakpoints[3].row, condition_breakpoint.position); + assert_eq!(loaded_breakpoints[3].message, condition_breakpoint.message); + assert_eq!( + loaded_breakpoints[3].condition, + condition_breakpoint.condition + ); + assert_eq!( + loaded_breakpoints[3].hit_condition, + condition_breakpoint.hit_condition + ); + assert_eq!(loaded_breakpoints[3].state, condition_breakpoint.state); + assert_eq!(loaded_breakpoints[3].path, Arc::from(path)); + + // hit condition breakpoint + assert_eq!(loaded_breakpoints[4].row, hit_condition_breakpoint.position); + assert_eq!( + loaded_breakpoints[4].message, + hit_condition_breakpoint.message + ); + assert_eq!( + loaded_breakpoints[4].condition, + hit_condition_breakpoint.condition + ); + assert_eq!( + loaded_breakpoints[4].hit_condition, + hit_condition_breakpoint.hit_condition + ); + assert_eq!(loaded_breakpoints[4].state, hit_condition_breakpoint.state); + assert_eq!(loaded_breakpoints[4].path, Arc::from(path)); } #[gpui::test]