diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4a93aae5df..5203bcbf1b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4151,6 +4151,7 @@ impl Editor { fn gutter_context_menu( &self, anchor: Anchor, + display_row: DisplayRow, window: &mut Window, cx: &mut Context, ) -> Entity { @@ -4213,6 +4214,15 @@ impl Editor { }; let has_bookmark = bookmark.as_ref().is_some(); + let clear_runnable_task_status = self + .runnable_task_key_for_display_row(display_row, window, cx) + .filter(|(buffer_id, buffer_row)| { + matches!( + self.runnable_task_status(*buffer_id, *buffer_row), + Some(RunnableTaskStatus::Passed | RunnableTaskStatus::Failed) + ) + }); + let run_to_cursor = window.is_action_available(&RunToCursor, cx); let toggle_state_entry: Option<(&str, Box)> = @@ -4231,6 +4241,22 @@ impl Editor { ContextMenu::build(window, cx, |menu, _, _cx| { menu.on_blur_subscription(Subscription::new(|| {})) .context(focus_handle) + .when_some( + clear_runnable_task_status, + |this, (buffer_id, buffer_row)| { + this.entry("Clear Run Status", None, { + let weak_editor = weak_editor.clone(); + move |_window, cx| { + weak_editor + .update(cx, |this, cx| { + this.clear_runnable_task_status(buffer_id, buffer_row, cx); + }) + .log_err(); + } + }) + .separator() + }, + ) .when(run_to_cursor, |this| { let weak_editor = weak_editor.clone(); this.entry( @@ -5847,7 +5873,7 @@ impl Editor { return; } - let context_menu = self.gutter_context_menu(anchor, window, cx); + let context_menu = self.gutter_context_menu(anchor, display_row, window, cx); self.mouse_context_menu = MouseContextMenu::pinned_to_editor( self, diff --git a/crates/editor/src/runnables.rs b/crates/editor/src/runnables.rs index 7e0cc27706..7ccecf101f 100644 --- a/crates/editor/src/runnables.rs +++ b/crates/editor/src/runnables.rs @@ -18,7 +18,8 @@ use ui::{Clickable as _, Color, IconButton, IconSize, Toggleable as _}; use crate::{ CodeActionSource, Editor, EditorSettings, EditorStyle, RangeToAnchorExt, SpawnNearestTask, - ToggleCodeActions, UPDATE_DEBOUNCE, display_map::DisplayRow, + ToggleCodeActions, UPDATE_DEBOUNCE, + display_map::{DisplayPoint, DisplayRow}, }; #[derive(Debug)] @@ -67,6 +68,10 @@ impl RunnableData { self.task_statuses.insert((buffer_id, buffer_row), status); } + pub fn clear_task_status(&mut self, (buffer_id, buffer_row): (BufferId, BufferRow)) { + self.task_statuses.remove(&(buffer_id, buffer_row)); + } + pub fn task_key_for_offset( &self, buffer_id: BufferId, @@ -441,6 +446,31 @@ impl Editor { cx.notify(); } + pub(crate) fn clear_runnable_task_status( + &mut self, + buffer_id: BufferId, + buffer_row: BufferRow, + cx: &mut Context, + ) { + self.runnables.clear_task_status((buffer_id, buffer_row)); + cx.notify(); + } + + pub(crate) fn runnable_task_key_for_display_row( + &self, + display_row: DisplayRow, + window: &mut Window, + cx: &mut Context, + ) -> Option<(BufferId, BufferRow)> { + let snapshot = self.snapshot(window, cx); + let multibuffer_row = + MultiBufferRow(DisplayPoint::new(display_row, 0).to_point(&snapshot).row); + let (buffer_snapshot, range) = snapshot + .buffer_snapshot() + .buffer_line_for_row(multibuffer_row)?; + Some((buffer_snapshot.remote_id(), range.start.row)) + } + pub(crate) fn runnable_task_key_for_offset( &self, buffer_id: BufferId,