mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-28 21:32:39 +00:00
Allow deployment of code actions from indicator
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
b1a44b5816
commit
7b28418979
2 changed files with 106 additions and 63 deletions
|
@ -125,7 +125,7 @@ action!(FoldSelectedRanges);
|
|||
action!(Scroll, Vector2F);
|
||||
action!(Select, SelectPhase);
|
||||
action!(ShowCompletions);
|
||||
action!(ShowCodeActions);
|
||||
action!(ShowCodeActions, bool);
|
||||
action!(ConfirmCompletion, Option<usize>);
|
||||
action!(ConfirmCodeAction, Option<usize>);
|
||||
|
||||
|
@ -251,7 +251,7 @@ pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpene
|
|||
Binding::new("alt-cmd-]", Unfold, Some("Editor")),
|
||||
Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")),
|
||||
Binding::new("ctrl-space", ShowCompletions, Some("Editor")),
|
||||
Binding::new("cmd-.", ShowCodeActions, Some("Editor")),
|
||||
Binding::new("cmd-.", ShowCodeActions(false), Some("Editor")),
|
||||
]);
|
||||
|
||||
cx.add_action(Editor::open_new);
|
||||
|
@ -430,7 +430,7 @@ pub struct Editor {
|
|||
context_menu: Option<ContextMenu>,
|
||||
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
|
||||
next_completion_id: CompletionId,
|
||||
available_code_actions: Option<CodeActionsMenu>,
|
||||
available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
|
||||
code_actions_task: Option<Task<()>>,
|
||||
}
|
||||
|
||||
|
@ -508,10 +508,15 @@ impl ContextMenu {
|
|||
}
|
||||
}
|
||||
|
||||
fn render(&self, build_settings: BuildSettings, cx: &AppContext) -> ElementBox {
|
||||
fn render(
|
||||
&self,
|
||||
cursor_position: DisplayPoint,
|
||||
build_settings: BuildSettings,
|
||||
cx: &AppContext,
|
||||
) -> (DisplayPoint, ElementBox) {
|
||||
match self {
|
||||
ContextMenu::Completions(menu) => menu.render(build_settings, cx),
|
||||
ContextMenu::CodeActions(menu) => menu.render(build_settings, cx),
|
||||
ContextMenu::Completions(menu) => (cursor_position, menu.render(build_settings, cx)),
|
||||
ContextMenu::CodeActions(menu) => menu.render(cursor_position, build_settings, cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -664,6 +669,7 @@ struct CodeActionsMenu {
|
|||
buffer: ModelHandle<Buffer>,
|
||||
selected_item: usize,
|
||||
list: UniformListState,
|
||||
deployed_from_indicator: bool,
|
||||
}
|
||||
|
||||
impl CodeActionsMenu {
|
||||
|
@ -685,51 +691,63 @@ impl CodeActionsMenu {
|
|||
!self.actions.is_empty()
|
||||
}
|
||||
|
||||
fn render(&self, build_settings: BuildSettings, cx: &AppContext) -> ElementBox {
|
||||
fn render(
|
||||
&self,
|
||||
mut cursor_position: DisplayPoint,
|
||||
build_settings: BuildSettings,
|
||||
cx: &AppContext,
|
||||
) -> (DisplayPoint, ElementBox) {
|
||||
enum ActionTag {}
|
||||
|
||||
let settings = build_settings(cx);
|
||||
let actions = self.actions.clone();
|
||||
let selected_item = self.selected_item;
|
||||
UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
|
||||
let settings = build_settings(cx);
|
||||
let start_ix = range.start;
|
||||
for (ix, action) in actions[range].iter().enumerate() {
|
||||
let item_ix = start_ix + ix;
|
||||
items.push(
|
||||
MouseEventHandler::new::<ActionTag, _, _, _>(item_ix, cx, |state, _| {
|
||||
let item_style = if item_ix == selected_item {
|
||||
settings.style.autocomplete.selected_item
|
||||
} else if state.hovered {
|
||||
settings.style.autocomplete.hovered_item
|
||||
} else {
|
||||
settings.style.autocomplete.item
|
||||
};
|
||||
let element =
|
||||
UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
|
||||
let settings = build_settings(cx);
|
||||
let start_ix = range.start;
|
||||
for (ix, action) in actions[range].iter().enumerate() {
|
||||
let item_ix = start_ix + ix;
|
||||
items.push(
|
||||
MouseEventHandler::new::<ActionTag, _, _, _>(item_ix, cx, |state, _| {
|
||||
let item_style = if item_ix == selected_item {
|
||||
settings.style.autocomplete.selected_item
|
||||
} else if state.hovered {
|
||||
settings.style.autocomplete.hovered_item
|
||||
} else {
|
||||
settings.style.autocomplete.item
|
||||
};
|
||||
|
||||
Text::new(action.lsp_action.title.clone(), settings.style.text.clone())
|
||||
.with_soft_wrap(false)
|
||||
.contained()
|
||||
.with_style(item_style)
|
||||
.boxed()
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_mouse_down(move |cx| {
|
||||
cx.dispatch_action(ConfirmCodeAction(Some(item_ix)));
|
||||
})
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
})
|
||||
.with_width_from_item(
|
||||
self.actions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|(_, action)| action.lsp_action.title.chars().count())
|
||||
.map(|(ix, _)| ix),
|
||||
)
|
||||
.contained()
|
||||
.with_style(settings.style.autocomplete.container)
|
||||
.boxed()
|
||||
Text::new(action.lsp_action.title.clone(), settings.style.text.clone())
|
||||
.with_soft_wrap(false)
|
||||
.contained()
|
||||
.with_style(item_style)
|
||||
.boxed()
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.on_mouse_down(move |cx| {
|
||||
cx.dispatch_action(ConfirmCodeAction(Some(item_ix)));
|
||||
})
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
})
|
||||
.with_width_from_item(
|
||||
self.actions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by_key(|(_, action)| action.lsp_action.title.chars().count())
|
||||
.map(|(ix, _)| ix),
|
||||
)
|
||||
.contained()
|
||||
.with_style(settings.style.autocomplete.container)
|
||||
.boxed();
|
||||
|
||||
if self.deployed_from_indicator {
|
||||
*cursor_position.column_mut() = 0;
|
||||
}
|
||||
|
||||
(cursor_position, element)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2060,7 +2078,11 @@ impl Editor {
|
|||
}))
|
||||
}
|
||||
|
||||
fn show_code_actions(&mut self, _: &ShowCodeActions, cx: &mut ViewContext<Self>) {
|
||||
fn show_code_actions(
|
||||
&mut self,
|
||||
&ShowCodeActions(deployed_from_indicator): &ShowCodeActions,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
let mut task = self.code_actions_task.take();
|
||||
cx.spawn_weak(|this, mut cx| async move {
|
||||
while let Some(prev_task) = task {
|
||||
|
@ -2073,8 +2095,17 @@ impl Editor {
|
|||
if let Some(this) = this.upgrade(&cx) {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if this.focused {
|
||||
if let Some(menu) = this.available_code_actions.clone() {
|
||||
this.show_context_menu(ContextMenu::CodeActions(menu), cx);
|
||||
if let Some((buffer, actions)) = this.available_code_actions.clone() {
|
||||
this.show_context_menu(
|
||||
ContextMenu::CodeActions(CodeActionsMenu {
|
||||
buffer,
|
||||
actions,
|
||||
selected_item: Default::default(),
|
||||
list: Default::default(),
|
||||
deployed_from_indicator,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2176,14 +2207,22 @@ impl Editor {
|
|||
}))
|
||||
}
|
||||
|
||||
pub fn render_code_actions_indicator(&self, cx: &AppContext) -> Option<ElementBox> {
|
||||
pub fn render_code_actions_indicator(&self, cx: &mut ViewContext<Self>) -> Option<ElementBox> {
|
||||
if self.available_code_actions.is_some() {
|
||||
enum Tag {}
|
||||
let style = (self.build_settings)(cx).style;
|
||||
Some(
|
||||
Svg::new("icons/zap.svg")
|
||||
.with_color(style.code_actions_indicator)
|
||||
.aligned()
|
||||
.boxed(),
|
||||
MouseEventHandler::new::<Tag, _, _, _>(0, cx, |_, _| {
|
||||
Svg::new("icons/zap.svg")
|
||||
.with_color(style.code_actions_indicator)
|
||||
.boxed()
|
||||
})
|
||||
.with_cursor_style(CursorStyle::PointingHand)
|
||||
.with_padding(Padding::uniform(3.))
|
||||
.on_mouse_down(|cx| {
|
||||
cx.dispatch_action(ShowCodeActions(true));
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
|
@ -2196,10 +2235,14 @@ impl Editor {
|
|||
.map_or(false, |menu| menu.visible())
|
||||
}
|
||||
|
||||
pub fn render_context_menu(&self, cx: &AppContext) -> Option<ElementBox> {
|
||||
pub fn render_context_menu(
|
||||
&self,
|
||||
cursor_position: DisplayPoint,
|
||||
cx: &AppContext,
|
||||
) -> Option<(DisplayPoint, ElementBox)> {
|
||||
self.context_menu
|
||||
.as_ref()
|
||||
.map(|menu| menu.render(self.build_settings.clone(), cx))
|
||||
.map(|menu| menu.render(cursor_position, self.build_settings.clone(), cx))
|
||||
}
|
||||
|
||||
fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
|
||||
|
@ -4418,12 +4461,7 @@ impl Editor {
|
|||
if actions.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(CodeActionsMenu {
|
||||
actions: actions.into(),
|
||||
buffer,
|
||||
selected_item: 0,
|
||||
list: Default::default(),
|
||||
})
|
||||
Some((buffer, actions.into()))
|
||||
}
|
||||
});
|
||||
cx.notify();
|
||||
|
|
|
@ -955,8 +955,7 @@ impl Element for EditorElement {
|
|||
|
||||
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
||||
if view.context_menu_visible() {
|
||||
let list = view.render_context_menu(cx).unwrap();
|
||||
context_menu = Some((newest_selection_head, list));
|
||||
context_menu = view.render_context_menu(newest_selection_head, cx);
|
||||
}
|
||||
|
||||
code_actions_indicator = view
|
||||
|
@ -1075,6 +1074,12 @@ impl Element for EditorElement {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some((_, indicator)) = &mut layout.code_actions_indicator {
|
||||
if indicator.dispatch_event(event, cx) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::LeftMouseDown {
|
||||
position,
|
||||
|
|
Loading…
Reference in a new issue