@@ -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(),
}
},
}
@@ -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(
@@ -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(),
}
}
}