Render code actions indicator

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2022-02-11 16:01:15 +01:00
parent 2fcdcac080
commit a41725daee
6 changed files with 74 additions and 22 deletions

View file

@ -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
.as_ref()
.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.add_selections_state = None;
self.select_next_state = None;
@ -4357,7 +4374,7 @@ impl Editor {
.unwrap();
self.push_to_nav_history(
old_cursor_position,
old_cursor_position.clone(),
Some(new_cursor_position.to_point(&buffer)),
cx,
);
@ -4386,6 +4403,12 @@ impl Editor {
}
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
.buffer
.read(cx)
@ -4894,6 +4917,7 @@ impl EditorSettings {
hint_diagnostic: default_diagnostic_style.clone(),
invalid_hint_diagnostic: default_diagnostic_style.clone(),
autocomplete: Default::default(),
code_actions_indicator: Default::default(),
}
},
}

View file

@ -281,7 +281,7 @@ impl EditorElement {
&mut self,
bounds: RectF,
visible_bounds: RectF,
layout: &LayoutState,
layout: &mut LayoutState,
cx: &mut PaintContext,
) {
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);
}
}
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(
@ -393,20 +401,20 @@ impl EditorElement {
}
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);
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 y = (position.row() + 1) as f32 * layout.line_height - scroll_top;
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() {
list_origin.set_y(list_origin.y() - layout.line_height - list_height);
}
completions_list.paint(
context_menu.paint(
list_origin,
RectF::from_points(Vector2F::zero(), vec2f(f32::MAX, f32::MAX)), // Let content bleed outside of editor
cx,
@ -918,7 +926,8 @@ impl Element for EditorElement {
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| {
let clamped = view.clamp_scroll_left(scroll_max.x());
let autoscrolled;
@ -939,21 +948,25 @@ impl Element for EditorElement {
snapshot = view.snapshot(cx);
}
if view.showing_context_menu() {
let newest_selection_head = view
.newest_selection::<usize>(&snapshot.buffer_snapshot)
.head()
.to_display_point(&snapshot);
let newest_selection_head = view
.newest_selection::<usize>(&snapshot.buffer_snapshot)
.head()
.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();
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() {
completions_list.layout(
if let Some((_, context_menu)) = context_menu.as_mut() {
context_menu.layout(
SizeConstraint {
min: Vector2F::zero(),
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(
start_row..end_row,
&snapshot,
@ -999,7 +1019,8 @@ impl Element for EditorElement {
em_width,
em_advance,
selections,
completions,
context_menu,
code_actions_indicator,
},
)
}
@ -1048,8 +1069,8 @@ impl Element for EditorElement {
paint: &mut PaintState,
cx: &mut EventContext,
) -> bool {
if let Some((_, completion_list)) = &mut layout.completions {
if completion_list.dispatch_event(event, cx) {
if let Some((_, context_menu)) = &mut layout.context_menu {
if context_menu.dispatch_event(event, cx) {
return true;
}
}
@ -1110,7 +1131,8 @@ pub struct LayoutState {
highlighted_ranges: Vec<(Range<DisplayPoint>, Color)>,
selections: HashMap<ReplicaId, Vec<text::Selection<DisplayPoint>>>,
text_offset: Vector2F,
completions: Option<(DisplayPoint, ElementBox)>,
context_menu: Option<(DisplayPoint, ElementBox)>,
code_actions_indicator: Option<(u32, ElementBox)>,
}
fn layout_line(

View file

@ -2475,7 +2475,7 @@ mod tests {
// Confirm a completion on the guest.
editor_b.next_notification(&cx_b).await;
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);
assert_eq!(editor.text(cx), "fn main() { a.first_method() }");
});

View file

@ -293,6 +293,7 @@ pub struct EditorStyle {
pub hint_diagnostic: DiagnosticStyle,
pub invalid_hint_diagnostic: DiagnosticStyle,
pub autocomplete: AutocompleteStyle,
pub code_actions_indicator: Color,
}
#[derive(Clone, Deserialize, Default)]
@ -420,6 +421,7 @@ impl InputEditorStyle {
hint_diagnostic: default_diagnostic_style.clone(),
invalid_hint_diagnostic: default_diagnostic_style.clone(),
autocomplete: Default::default(),
code_actions_indicator: Default::default(),
}
}
}

View 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

View file

@ -253,6 +253,7 @@ line_number_active = "$text.0.color"
selection = "$selection.host"
guest_selections = "$selection.guests"
error_color = "$status.bad"
code_actions_indicator = "$text.3.color"
[editor.diagnostic_path_header]
background = "$state.active_line"