Add Code Actions to the Toolbar (#31236)
Closes issue #31120. https://github.com/user-attachments/assets/a4b3c86d-7358-49ac-b8d9-e9af50daf671 Release Notes: - Added a code actions icon to the toolbar. This icon can be disabled by setting `toolbar.code_actions` to `false`.
This commit is contained in:
parent
fbc922ad46
commit
1cad1cbbfc
17 changed files with 167 additions and 50 deletions
|
@ -74,16 +74,22 @@ pub struct SelectToEndOfLine {
|
|||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ToggleCodeActions {
|
||||
// Display row from which the action was deployed.
|
||||
// Source from which the action was deployed.
|
||||
#[serde(default)]
|
||||
#[serde(skip)]
|
||||
pub deployed_from_indicator: Option<DisplayRow>,
|
||||
pub deployed_from: Option<CodeActionSource>,
|
||||
// Run first available task if there is only one.
|
||||
#[serde(default)]
|
||||
#[serde(skip)]
|
||||
pub quick_launch: bool,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
pub enum CodeActionSource {
|
||||
Indicator(DisplayRow),
|
||||
QuickActionBar,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfirmCompletion {
|
||||
|
|
|
@ -26,6 +26,7 @@ use task::ResolvedTask;
|
|||
use ui::{Color, IntoElement, ListItem, Pixels, Popover, Styled, prelude::*};
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::CodeActionSource;
|
||||
use crate::editor_settings::SnippetSortOrder;
|
||||
use crate::hover_popover::{hover_markdown_style, open_markdown_url};
|
||||
use crate::{
|
||||
|
@ -168,6 +169,7 @@ impl CodeContextMenu {
|
|||
pub enum ContextMenuOrigin {
|
||||
Cursor,
|
||||
GutterIndicator(DisplayRow),
|
||||
QuickActionBar,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -840,7 +842,7 @@ pub struct AvailableCodeAction {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct CodeActionContents {
|
||||
pub struct CodeActionContents {
|
||||
tasks: Option<Rc<ResolvedTasks>>,
|
||||
actions: Option<Rc<[AvailableCodeAction]>>,
|
||||
debug_scenarios: Vec<DebugScenario>,
|
||||
|
@ -968,12 +970,12 @@ impl CodeActionsItem {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct CodeActionsMenu {
|
||||
pub struct CodeActionsMenu {
|
||||
pub actions: CodeActionContents,
|
||||
pub buffer: Entity<Buffer>,
|
||||
pub selected_item: usize,
|
||||
pub scroll_handle: UniformListScrollHandle,
|
||||
pub deployed_from_indicator: Option<DisplayRow>,
|
||||
pub deployed_from: Option<CodeActionSource>,
|
||||
}
|
||||
|
||||
impl CodeActionsMenu {
|
||||
|
@ -1042,10 +1044,10 @@ impl CodeActionsMenu {
|
|||
}
|
||||
|
||||
fn origin(&self) -> ContextMenuOrigin {
|
||||
if let Some(row) = self.deployed_from_indicator {
|
||||
ContextMenuOrigin::GutterIndicator(row)
|
||||
} else {
|
||||
ContextMenuOrigin::Cursor
|
||||
match &self.deployed_from {
|
||||
Some(CodeActionSource::Indicator(row)) => ContextMenuOrigin::GutterIndicator(*row),
|
||||
Some(CodeActionSource::QuickActionBar) => ContextMenuOrigin::QuickActionBar,
|
||||
None => ContextMenuOrigin::Cursor,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
pub mod actions;
|
||||
mod blink_manager;
|
||||
mod clangd_ext;
|
||||
mod code_context_menus;
|
||||
pub mod code_context_menus;
|
||||
pub mod display_map;
|
||||
mod editor_settings;
|
||||
mod editor_settings_controls;
|
||||
|
@ -777,7 +777,7 @@ impl RunnableTasks {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ResolvedTasks {
|
||||
pub struct ResolvedTasks {
|
||||
templates: SmallVec<[(TaskSourceKind, ResolvedTask); 1]>,
|
||||
position: Anchor,
|
||||
}
|
||||
|
@ -5375,7 +5375,7 @@ impl Editor {
|
|||
let quick_launch = action.quick_launch;
|
||||
let mut context_menu = self.context_menu.borrow_mut();
|
||||
if let Some(CodeContextMenu::CodeActions(code_actions)) = context_menu.as_ref() {
|
||||
if code_actions.deployed_from_indicator == action.deployed_from_indicator {
|
||||
if code_actions.deployed_from == action.deployed_from {
|
||||
// Toggle if we're selecting the same one
|
||||
*context_menu = None;
|
||||
cx.notify();
|
||||
|
@ -5388,7 +5388,7 @@ impl Editor {
|
|||
}
|
||||
drop(context_menu);
|
||||
let snapshot = self.snapshot(window, cx);
|
||||
let deployed_from_indicator = action.deployed_from_indicator;
|
||||
let deployed_from = action.deployed_from.clone();
|
||||
let mut task = self.code_actions_task.take();
|
||||
let action = action.clone();
|
||||
cx.spawn_in(window, async move |editor, cx| {
|
||||
|
@ -5399,10 +5399,12 @@ impl Editor {
|
|||
|
||||
let spawned_test_task = editor.update_in(cx, |editor, window, cx| {
|
||||
if editor.focus_handle.is_focused(window) {
|
||||
let multibuffer_point = action
|
||||
.deployed_from_indicator
|
||||
.map(|row| DisplayPoint::new(row, 0).to_point(&snapshot))
|
||||
.unwrap_or_else(|| editor.selections.newest::<Point>(cx).head());
|
||||
let multibuffer_point = match &action.deployed_from {
|
||||
Some(CodeActionSource::Indicator(row)) => {
|
||||
DisplayPoint::new(*row, 0).to_point(&snapshot)
|
||||
}
|
||||
_ => editor.selections.newest::<Point>(cx).head(),
|
||||
};
|
||||
let (buffer, buffer_row) = snapshot
|
||||
.buffer_snapshot
|
||||
.buffer_line_for_row(MultiBufferRow(multibuffer_point.row))
|
||||
|
@ -5526,7 +5528,7 @@ impl Editor {
|
|||
),
|
||||
selected_item: Default::default(),
|
||||
scroll_handle: UniformListScrollHandle::default(),
|
||||
deployed_from_indicator,
|
||||
deployed_from,
|
||||
}));
|
||||
if spawn_straight_away {
|
||||
if let Some(task) = editor.confirm_code_action(
|
||||
|
@ -5746,6 +5748,21 @@ impl Editor {
|
|||
self.refresh_code_actions(window, cx);
|
||||
}
|
||||
|
||||
pub fn code_actions_enabled(&self, cx: &App) -> bool {
|
||||
!self.code_action_providers.is_empty()
|
||||
&& EditorSettings::get_global(cx).toolbar.code_actions
|
||||
}
|
||||
|
||||
pub fn has_available_code_actions(&self) -> bool {
|
||||
self.available_code_actions
|
||||
.as_ref()
|
||||
.is_some_and(|(_, actions)| !actions.is_empty())
|
||||
}
|
||||
|
||||
pub fn context_menu(&self) -> &RefCell<Option<CodeContextMenu>> {
|
||||
&self.context_menu
|
||||
}
|
||||
|
||||
fn refresh_code_actions(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<()> {
|
||||
let newest_selection = self.selections.newest_anchor().clone();
|
||||
let newest_selection_adjusted = self.selections.newest_adjusted(cx).clone();
|
||||
|
@ -7498,7 +7515,7 @@ impl Editor {
|
|||
window.focus(&editor.focus_handle(cx));
|
||||
editor.toggle_code_actions(
|
||||
&ToggleCodeActions {
|
||||
deployed_from_indicator: Some(row),
|
||||
deployed_from: Some(CodeActionSource::Indicator(row)),
|
||||
quick_launch,
|
||||
},
|
||||
window,
|
||||
|
@ -7519,7 +7536,7 @@ impl Editor {
|
|||
.map_or(false, |menu| menu.visible())
|
||||
}
|
||||
|
||||
fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
|
||||
pub fn context_menu_origin(&self) -> Option<ContextMenuOrigin> {
|
||||
self.context_menu
|
||||
.borrow()
|
||||
.as_ref()
|
||||
|
@ -8538,7 +8555,7 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_context_menu(
|
||||
pub fn render_context_menu(
|
||||
&self,
|
||||
style: &EditorStyle,
|
||||
max_height_in_lines: u32,
|
||||
|
|
|
@ -109,6 +109,7 @@ pub struct Toolbar {
|
|||
pub quick_actions: bool,
|
||||
pub selections_menu: bool,
|
||||
pub agent_review: bool,
|
||||
pub code_actions: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
|
||||
|
@ -503,6 +504,10 @@ pub struct ToolbarContent {
|
|||
///
|
||||
/// Default: true
|
||||
pub agent_review: Option<bool>,
|
||||
/// Whether to display code action buttons in the editor toolbar.
|
||||
///
|
||||
/// Default: true
|
||||
pub code_actions: Option<bool>,
|
||||
}
|
||||
|
||||
/// Scrollbar related settings
|
||||
|
|
|
@ -14243,7 +14243,7 @@ async fn test_context_menus_hide_hover_popover(cx: &mut gpui::TestAppContext) {
|
|||
cx.update_editor(|editor, window, cx| {
|
||||
editor.toggle_code_actions(
|
||||
&ToggleCodeActions {
|
||||
deployed_from_indicator: None,
|
||||
deployed_from: None,
|
||||
quick_launch: false,
|
||||
},
|
||||
window,
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::{
|
||||
ActiveDiagnostic, BlockId, COLUMNAR_SELECTION_MODIFIERS, CURSORS_VISIBLE_FOR,
|
||||
ChunkRendererContext, ChunkReplacement, ConflictsOurs, ConflictsOursMarker, ConflictsOuter,
|
||||
ConflictsTheirs, ConflictsTheirsMarker, ContextMenuPlacement, CursorShape, CustomBlockId,
|
||||
DisplayDiffHunk, DisplayPoint, DisplayRow, DocumentHighlightRead, DocumentHighlightWrite,
|
||||
EditDisplayMode, Editor, EditorMode, EditorSettings, EditorSnapshot, EditorStyle,
|
||||
FILE_HEADER_HEIGHT, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp, HandleInput,
|
||||
HoveredCursor, InlayHintRefreshReason, InlineCompletion, JumpData, LineDown, LineHighlight,
|
||||
LineUp, MAX_LINE_LEN, MIN_LINE_NUMBER_DIGITS, MINIMAP_FONT_SIZE,
|
||||
ChunkRendererContext, ChunkReplacement, CodeActionSource, ConflictsOurs, ConflictsOursMarker,
|
||||
ConflictsOuter, ConflictsTheirs, ConflictsTheirsMarker, ContextMenuPlacement, CursorShape,
|
||||
CustomBlockId, DisplayDiffHunk, DisplayPoint, DisplayRow, DocumentHighlightRead,
|
||||
DocumentHighlightWrite, EditDisplayMode, Editor, EditorMode, EditorSettings, EditorSnapshot,
|
||||
EditorStyle, FILE_HEADER_HEIGHT, FocusedBlock, GutterDimensions, HalfPageDown, HalfPageUp,
|
||||
HandleInput, HoveredCursor, InlayHintRefreshReason, InlineCompletion, JumpData, LineDown,
|
||||
LineHighlight, LineUp, MAX_LINE_LEN, MIN_LINE_NUMBER_DIGITS, MINIMAP_FONT_SIZE,
|
||||
MULTI_BUFFER_EXCERPT_HEADER_HEIGHT, OpenExcerpts, PageDown, PageUp, PhantomBreakpointIndicator,
|
||||
Point, RowExt, RowRangeExt, SelectPhase, SelectedTextHighlight, Selection, SoftWrap,
|
||||
StickyHeaderExcerpt, ToPoint, ToggleFold,
|
||||
|
@ -2385,7 +2385,7 @@ impl EditorElement {
|
|||
self.editor.update(cx, |editor, cx| {
|
||||
let active_task_indicator_row =
|
||||
if let Some(crate::CodeContextMenu::CodeActions(CodeActionsMenu {
|
||||
deployed_from_indicator,
|
||||
deployed_from,
|
||||
actions,
|
||||
..
|
||||
})) = editor.context_menu.borrow().as_ref()
|
||||
|
@ -2393,7 +2393,10 @@ impl EditorElement {
|
|||
actions
|
||||
.tasks()
|
||||
.map(|tasks| tasks.position.to_display_point(snapshot).row())
|
||||
.or(*deployed_from_indicator)
|
||||
.or_else(|| match deployed_from {
|
||||
Some(CodeActionSource::Indicator(row)) => Some(*row),
|
||||
_ => None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
@ -226,7 +226,7 @@ pub fn deploy_context_menu(
|
|||
.action(
|
||||
"Show Code Actions",
|
||||
Box::new(ToggleCodeActions {
|
||||
deployed_from_indicator: None,
|
||||
deployed_from: None,
|
||||
quick_launch: false,
|
||||
}),
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue