From 24ad60a65103afc1019adb1e3b6bfb4cf9fbaf07 Mon Sep 17 00:00:00 2001 From: Isaac Clayton Date: Mon, 30 May 2022 12:27:06 +0200 Subject: [PATCH] Add hover action and style context menu --- crates/editor/src/editor.rs | 39 +++++++++++++++++++++++------------- crates/editor/src/element.rs | 29 +++++++++++++++++++++------ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 0707f71560..1b938e5525 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -80,6 +80,15 @@ pub struct Scroll(pub Vector2F); #[derive(Clone, PartialEq)] pub struct Select(pub SelectPhase); +#[derive(Clone)] +pub struct ShowHover(DisplayPoint); + +#[derive(Clone)] +pub struct Hover { + point: DisplayPoint, + overshoot: DisplayPoint, +} + #[derive(Clone, Deserialize, PartialEq)] pub struct Input(pub String); @@ -191,7 +200,6 @@ actions!( UnfoldLines, FoldSelectedRanges, ShowCompletions, - ShowHover, OpenExcerpts, RestartLanguageServer, ] @@ -210,7 +218,7 @@ impl_actions!( ] ); -impl_internal_actions!(editor, [Scroll, Select, GoToDefinitionAt]); +impl_internal_actions!(editor, [Scroll, Select, Hover, ShowHover, GoToDefinitionAt]); enum DocumentHighlightRead {} enum DocumentHighlightWrite {} @@ -297,6 +305,8 @@ pub fn init(cx: &mut MutableAppContext) { cx.add_action(Editor::fold_selected_ranges); cx.add_action(Editor::show_completions); cx.add_action(Editor::toggle_code_actions); + cx.add_action(Editor::hover); + cx.add_action(Editor::show_hover); cx.add_action(Editor::open_excerpts); cx.add_action(Editor::restart_language_server); cx.add_async_action(Editor::confirm_completion); @@ -855,16 +865,19 @@ impl CodeActionsMenu { #[derive(Clone)] struct HoverPopover { + pub point: DisplayPoint, pub text: String, pub runs: Vec<(Range, HighlightStyle)>, } impl HoverPopover { fn render(&self, style: EditorStyle) -> ElementBox { + let container_style = style.autocomplete.container; Text::new(self.text.clone(), style.text.clone()) .with_soft_wrap(false) .with_highlights(self.runs.clone()) .contained() + .with_style(container_style) .boxed() } } @@ -1026,6 +1039,7 @@ impl Editor { next_completion_id: 0, available_code_actions: Default::default(), code_actions_task: Default::default(), + hover_task: Default::default(), document_highlights_task: Default::default(), pending_rename: Default::default(), searchable: true, @@ -2408,7 +2422,13 @@ impl Editor { })) } - fn show_hover(&mut self, _: &ShowHover, cx: &mut ViewContext) { + fn hover(&mut self, action: &Hover, cx: &mut ViewContext) { + if action.overshoot.is_zero() { + self.show_hover(&ShowHover(action.point), cx); + } + } + + fn show_hover(&mut self, action: &ShowHover, cx: &mut ViewContext) { if self.pending_rename.is_some() { return; } @@ -2419,18 +2439,9 @@ impl Editor { return; }; - let position = self.selections.newest_anchor().head(); - let (buffer, buffer_position) = if let Some(output) = self - .buffer - .read(cx) - .text_anchor_for_position(position.clone(), cx) - { - output - } else { - return; - }; - let hover = HoverPopover { + // TODO: beginning of symbol based on range + point: action.0, text: "Test hover information".to_string(), runs: Vec::new(), }; diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 5555a8fb11..73b9b28e82 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -5,7 +5,7 @@ use super::{ }; use crate::{ display_map::{DisplaySnapshot, TransformBlock}, - EditorStyle, GoToDefinition, + EditorStyle, GoToDefinition, Hover, }; use clock::ReplicaId; use collections::{BTreeMap, HashMap}; @@ -126,7 +126,7 @@ impl EditorElement { } else if shift && alt { cx.dispatch_action(Select(SelectPhase::BeginColumnar { position, - overshoot, + overshoot: overshoot.column(), })); } else if shift { cx.dispatch_action(Select(SelectPhase::Extend { @@ -195,7 +195,7 @@ impl EditorElement { cx.dispatch_action(Select(SelectPhase::Update { position, - overshoot, + overshoot: overshoot.column(), scroll_position: (snapshot.scroll_position() + scroll_delta) .clamp(Vector2F::zero(), layout.scroll_max), })); @@ -1251,6 +1251,16 @@ impl Element for EditorElement { precise, } => self.scroll(*position, *delta, *precise, layout, paint, cx), Event::KeyDown { input, .. } => self.key_down(input.as_deref(), cx), + Event::MouseMoved { position, .. } => { + if paint.text_bounds.contains_point(*position) { + let (point, overshoot) = + paint.point_for_position(&self.snapshot(cx), layout, *position); + cx.dispatch_action(Hover { point, overshoot }); + true + } else { + false + } + } _ => false, } } @@ -1329,16 +1339,20 @@ pub struct PaintState { } impl PaintState { + /// Returns two display points. The first is the nearest valid + /// position in the current buffer and the second is the distance to the + /// nearest valid position if there was overshoot. fn point_for_position( &self, snapshot: &EditorSnapshot, layout: &LayoutState, position: Vector2F, - ) -> (DisplayPoint, u32) { + ) -> (DisplayPoint, DisplayPoint) { let scroll_position = snapshot.scroll_position(); let position = position - self.text_bounds.origin(); let y = position.y().max(0.0).min(layout.size.y()); let row = ((y / layout.line_height) + scroll_position.y()) as u32; + let row_overshoot = row.saturating_sub(snapshot.max_point().row()); let row = cmp::min(row, snapshot.max_point().row()); let line = &layout.line_layouts[(row - scroll_position.y() as u32) as usize]; let x = position.x() + (scroll_position.x() * layout.em_width); @@ -1350,9 +1364,12 @@ impl PaintState { } else { 0 }; - let overshoot = (0f32.max(x - line.width()) / layout.em_advance) as u32; + let column_overshoot = (0f32.max(x - line.width()) / layout.em_advance) as u32; - (DisplayPoint::new(row, column), overshoot) + ( + DisplayPoint::new(row, column), + DisplayPoint::new(row_overshoot, column_overshoot), + ) } }