Render code actions indicator
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
2fcdcac080
commit
a41725daee
6 changed files with 74 additions and 22 deletions
|
@ -2177,7 +2177,21 @@ impl Editor {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn showing_context_menu(&self) -> bool {
|
pub fn render_code_actions_indicator(&self, cx: &AppContext) -> Option<ElementBox> {
|
||||||
|
if self.available_code_actions.is_some() {
|
||||||
|
let style = (self.build_settings)(cx).style;
|
||||||
|
Some(
|
||||||
|
Svg::new("icons/zap.svg")
|
||||||
|
.with_color(style.code_actions_indicator)
|
||||||
|
.aligned()
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn context_menu_visible(&self) -> bool {
|
||||||
self.context_menu
|
self.context_menu
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(false, |menu| menu.visible())
|
.map_or(false, |menu| menu.visible())
|
||||||
|
@ -4341,7 +4355,10 @@ impl Editor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let buffer = self.buffer.read(cx).snapshot(cx);
|
let display_map = self
|
||||||
|
.display_map
|
||||||
|
.update(cx, |display_map, cx| display_map.snapshot(cx));
|
||||||
|
let buffer = &display_map.buffer_snapshot;
|
||||||
self.pending_selection = None;
|
self.pending_selection = None;
|
||||||
self.add_selections_state = None;
|
self.add_selections_state = None;
|
||||||
self.select_next_state = None;
|
self.select_next_state = None;
|
||||||
|
@ -4357,7 +4374,7 @@ impl Editor {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.push_to_nav_history(
|
self.push_to_nav_history(
|
||||||
old_cursor_position,
|
old_cursor_position.clone(),
|
||||||
Some(new_cursor_position.to_point(&buffer)),
|
Some(new_cursor_position.to_point(&buffer)),
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -4386,6 +4403,12 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(project) = self.project.as_ref() {
|
if let Some(project) = self.project.as_ref() {
|
||||||
|
if old_cursor_position.to_display_point(&display_map).row()
|
||||||
|
!= new_cursor_position.to_display_point(&display_map).row()
|
||||||
|
{
|
||||||
|
self.available_code_actions.take();
|
||||||
|
}
|
||||||
|
|
||||||
let (buffer, head) = self
|
let (buffer, head) = self
|
||||||
.buffer
|
.buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -4894,6 +4917,7 @@ impl EditorSettings {
|
||||||
hint_diagnostic: default_diagnostic_style.clone(),
|
hint_diagnostic: default_diagnostic_style.clone(),
|
||||||
invalid_hint_diagnostic: default_diagnostic_style.clone(),
|
invalid_hint_diagnostic: default_diagnostic_style.clone(),
|
||||||
autocomplete: Default::default(),
|
autocomplete: Default::default(),
|
||||||
|
code_actions_indicator: Default::default(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,7 +281,7 @@ impl EditorElement {
|
||||||
&mut self,
|
&mut self,
|
||||||
bounds: RectF,
|
bounds: RectF,
|
||||||
visible_bounds: RectF,
|
visible_bounds: RectF,
|
||||||
layout: &LayoutState,
|
layout: &mut LayoutState,
|
||||||
cx: &mut PaintContext,
|
cx: &mut PaintContext,
|
||||||
) {
|
) {
|
||||||
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
|
let scroll_top = layout.snapshot.scroll_position().y() * layout.line_height;
|
||||||
|
@ -295,6 +295,14 @@ impl EditorElement {
|
||||||
line.paint(line_origin, visible_bounds, layout.line_height, cx);
|
line.paint(line_origin, visible_bounds, layout.line_height, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some((row, indicator)) = layout.code_actions_indicator.as_mut() {
|
||||||
|
let mut x = bounds.width() - layout.gutter_padding;
|
||||||
|
let mut y = *row as f32 * layout.line_height - scroll_top;
|
||||||
|
x += ((layout.gutter_padding + layout.text_offset.x()) - indicator.size().x()) / 2.;
|
||||||
|
y += (layout.line_height - indicator.size().y()) / 2.;
|
||||||
|
indicator.paint(bounds.origin() + vec2f(x, y), visible_bounds, cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint_text(
|
fn paint_text(
|
||||||
|
@ -393,20 +401,20 @@ impl EditorElement {
|
||||||
}
|
}
|
||||||
cx.scene.pop_layer();
|
cx.scene.pop_layer();
|
||||||
|
|
||||||
if let Some((position, completions_list)) = layout.completions.as_mut() {
|
if let Some((position, context_menu)) = layout.context_menu.as_mut() {
|
||||||
cx.scene.push_stacking_context(None);
|
cx.scene.push_stacking_context(None);
|
||||||
|
|
||||||
let cursor_row_layout = &layout.line_layouts[(position.row() - start_row) as usize];
|
let cursor_row_layout = &layout.line_layouts[(position.row() - start_row) as usize];
|
||||||
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
|
let x = cursor_row_layout.x_for_index(position.column() as usize) - scroll_left;
|
||||||
let y = (position.row() + 1) as f32 * layout.line_height - scroll_top;
|
let y = (position.row() + 1) as f32 * layout.line_height - scroll_top;
|
||||||
let mut list_origin = content_origin + vec2f(x, y);
|
let mut list_origin = content_origin + vec2f(x, y);
|
||||||
let list_height = completions_list.size().y();
|
let list_height = context_menu.size().y();
|
||||||
|
|
||||||
if list_origin.y() + list_height > bounds.lower_left().y() {
|
if list_origin.y() + list_height > bounds.lower_left().y() {
|
||||||
list_origin.set_y(list_origin.y() - layout.line_height - list_height);
|
list_origin.set_y(list_origin.y() - layout.line_height - list_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
completions_list.paint(
|
context_menu.paint(
|
||||||
list_origin,
|
list_origin,
|
||||||
RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
|
RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
|
||||||
cx,
|
cx,
|
||||||
|
@ -918,7 +926,8 @@ impl Element for EditorElement {
|
||||||
max_row.saturating_sub(1) as f32,
|
max_row.saturating_sub(1) as f32,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut completions = None;
|
let mut context_menu = None;
|
||||||
|
let mut code_actions_indicator = None;
|
||||||
self.update_view(cx.app, |view, cx| {
|
self.update_view(cx.app, |view, cx| {
|
||||||
let clamped = view.clamp_scroll_left(scroll_max.x());
|
let clamped = view.clamp_scroll_left(scroll_max.x());
|
||||||
let autoscrolled;
|
let autoscrolled;
|
||||||
|
@ -939,21 +948,25 @@ impl Element for EditorElement {
|
||||||
snapshot = view.snapshot(cx);
|
snapshot = view.snapshot(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if view.showing_context_menu() {
|
|
||||||
let newest_selection_head = view
|
let newest_selection_head = view
|
||||||
.newest_selection::<usize>(&snapshot.buffer_snapshot)
|
.newest_selection::<usize>(&snapshot.buffer_snapshot)
|
||||||
.head()
|
.head()
|
||||||
.to_display_point(&snapshot);
|
.to_display_point(&snapshot);
|
||||||
|
|
||||||
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
if (start_row..end_row).contains(&newest_selection_head.row()) {
|
||||||
|
if view.context_menu_visible() {
|
||||||
let list = view.render_context_menu(cx).unwrap();
|
let list = view.render_context_menu(cx).unwrap();
|
||||||
completions = Some((newest_selection_head, list));
|
context_menu = Some((newest_selection_head, list));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code_actions_indicator = view
|
||||||
|
.render_code_actions_indicator(cx)
|
||||||
|
.map(|indicator| (newest_selection_head.row(), indicator));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some((_, completions_list)) = completions.as_mut() {
|
if let Some((_, context_menu)) = context_menu.as_mut() {
|
||||||
completions_list.layout(
|
context_menu.layout(
|
||||||
SizeConstraint {
|
SizeConstraint {
|
||||||
min: Vector2F::zero(),
|
min: Vector2F::zero(),
|
||||||
max: vec2f(
|
max: vec2f(
|
||||||
|
@ -965,6 +978,13 @@ impl Element for EditorElement {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some((_, indicator)) = code_actions_indicator.as_mut() {
|
||||||
|
indicator.layout(
|
||||||
|
SizeConstraint::strict_along(Axis::Vertical, line_height * 0.618),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let blocks = self.layout_blocks(
|
let blocks = self.layout_blocks(
|
||||||
start_row..end_row,
|
start_row..end_row,
|
||||||
&snapshot,
|
&snapshot,
|
||||||
|
@ -999,7 +1019,8 @@ impl Element for EditorElement {
|
||||||
em_width,
|
em_width,
|
||||||
em_advance,
|
em_advance,
|
||||||
selections,
|
selections,
|
||||||
completions,
|
context_menu,
|
||||||
|
code_actions_indicator,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1048,8 +1069,8 @@ impl Element for EditorElement {
|
||||||
paint: &mut PaintState,
|
paint: &mut PaintState,
|
||||||
cx: &mut EventContext,
|
cx: &mut EventContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some((_, completion_list)) = &mut layout.completions {
|
if let Some((_, context_menu)) = &mut layout.context_menu {
|
||||||
if completion_list.dispatch_event(event, cx) {
|
if context_menu.dispatch_event(event, cx) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1110,7 +1131,8 @@ pub struct LayoutState {
|
||||||
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
|
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
|
||||||
selections: HashMap<ReplicaId, Vec<text::Selection<DisplayPoint>>>,
|
selections: HashMap<ReplicaId, Vec<text::Selection<DisplayPoint>>>,
|
||||||
text_offset: Vector2F,
|
text_offset: Vector2F,
|
||||||
completions: Option<(DisplayPoint, ElementBox)>,
|
context_menu: Option<(DisplayPoint, ElementBox)>,
|
||||||
|
code_actions_indicator: Option<(u32, ElementBox)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout_line(
|
fn layout_line(
|
||||||
|
|
|
@ -2475,7 +2475,7 @@ mod tests {
|
||||||
// Confirm a completion on the guest.
|
// Confirm a completion on the guest.
|
||||||
editor_b.next_notification(&cx_b).await;
|
editor_b.next_notification(&cx_b).await;
|
||||||
editor_b.update(&mut cx_b, |editor, cx| {
|
editor_b.update(&mut cx_b, |editor, cx| {
|
||||||
assert!(editor.showing_context_menu());
|
assert!(editor.context_menu_visible());
|
||||||
editor.confirm_completion(&ConfirmCompletion(Some(0)), cx);
|
editor.confirm_completion(&ConfirmCompletion(Some(0)), cx);
|
||||||
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
|
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
|
||||||
});
|
});
|
||||||
|
|
|
@ -293,6 +293,7 @@ pub struct EditorStyle {
|
||||||
pub hint_diagnostic: DiagnosticStyle,
|
pub hint_diagnostic: DiagnosticStyle,
|
||||||
pub invalid_hint_diagnostic: DiagnosticStyle,
|
pub invalid_hint_diagnostic: DiagnosticStyle,
|
||||||
pub autocomplete: AutocompleteStyle,
|
pub autocomplete: AutocompleteStyle,
|
||||||
|
pub code_actions_indicator: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
|
@ -420,6 +421,7 @@ impl InputEditorStyle {
|
||||||
hint_diagnostic: default_diagnostic_style.clone(),
|
hint_diagnostic: default_diagnostic_style.clone(),
|
||||||
invalid_hint_diagnostic: default_diagnostic_style.clone(),
|
invalid_hint_diagnostic: default_diagnostic_style.clone(),
|
||||||
autocomplete: Default::default(),
|
autocomplete: Default::default(),
|
||||||
|
code_actions_indicator: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
3
crates/zed/assets/icons/zap.svg
Normal file
3
crates/zed/assets/icons/zap.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2.00262 12L2.89358 7.9886C2.95207 7.71862 2.77658 7.49963 2.5021 7.49963H0.000671387L6.00037 0L5.10792 4.0108C5.04792 4.27929 5.22341 4.49828 5.4994 4.49828H7.99932L1.99962 11.9979L2.00262 12Z" fill="#FDE047"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 322 B |
|
@ -253,6 +253,7 @@ line_number_active = "$text.0.color"
|
||||||
selection = "$selection.host"
|
selection = "$selection.host"
|
||||||
guest_selections = "$selection.guests"
|
guest_selections = "$selection.guests"
|
||||||
error_color = "$status.bad"
|
error_color = "$status.bad"
|
||||||
|
code_actions_indicator = "$text.3.color"
|
||||||
|
|
||||||
[editor.diagnostic_path_header]
|
[editor.diagnostic_path_header]
|
||||||
background = "$state.active_line"
|
background = "$state.active_line"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue