From a41725daee7eb22e255556024004b2cf907eab5f Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 11 Feb 2022 16:01:15 +0100 Subject: [PATCH] Render code actions indicator Co-Authored-By: Nathan Sobo --- crates/editor/src/editor.rs | 30 +++++++++++++-- crates/editor/src/element.rs | 58 ++++++++++++++++++++--------- crates/server/src/rpc.rs | 2 +- crates/theme/src/theme.rs | 2 + crates/zed/assets/icons/zap.svg | 3 ++ crates/zed/assets/themes/_base.toml | 1 + 6 files changed, 74 insertions(+), 22 deletions(-) create mode 100644 crates/zed/assets/icons/zap.svg diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c10650fefc292cb82d7365575687df99ce4ea31a..b34991e4f5675dee029fd61d12055259b2db770d 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2177,7 +2177,21 @@ impl Editor { })) } - pub fn showing_context_menu(&self) -> bool { + pub fn render_code_actions_indicator(&self, cx: &AppContext) -> Option { + 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(), } }, } diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 82365445e60ff53dad97aaf225dc3e7166b7f307..1965f76c880beb7b4e07f4ece445bad3c84bd32f 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -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::(&snapshot.buffer_snapshot) - .head() - .to_display_point(&snapshot); + let newest_selection_head = view + .newest_selection::(&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, Color)>, selections: HashMap>>, text_offset: Vector2F, - completions: Option<(DisplayPoint, ElementBox)>, + context_menu: Option<(DisplayPoint, ElementBox)>, + code_actions_indicator: Option<(u32, ElementBox)>, } fn layout_line( diff --git a/crates/server/src/rpc.rs b/crates/server/src/rpc.rs index ce0270cc751a6c80f56c8883c58b462026632317..72edd9659f7b8cf0f6de9bc3fdff3406c649ddb5 100644 --- a/crates/server/src/rpc.rs +++ b/crates/server/src/rpc.rs @@ -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() }"); }); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 339cabbbf3241516685f34899ca7f2cd857d6220..db078cd862e5f055550971ccbad0aa169dc244e9 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -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(), } } } diff --git a/crates/zed/assets/icons/zap.svg b/crates/zed/assets/icons/zap.svg new file mode 100644 index 0000000000000000000000000000000000000000..8d517dcb53db7159e8a7133b19ce5ee0f8f293b5 --- /dev/null +++ b/crates/zed/assets/icons/zap.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/zed/assets/themes/_base.toml b/crates/zed/assets/themes/_base.toml index 2f3541dc1b3b0943034dab8dcca00100a07ad989..389f4571070cc3dd2274fd1cfc9ed5f30f75699d 100644 --- a/crates/zed/assets/themes/_base.toml +++ b/crates/zed/assets/themes/_base.toml @@ -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"