gpui_base/input/editor/lsp/
hover.rs1use anyhow::Result;
2use gpui::{App, Context, MouseMoveEvent, Task, Window};
3use instant::Duration;
4use ropey::Rope;
5
6use crate::input::{EditorMode, HoverPopoverState, InputBaseState, RopeExt};
7
8pub trait HoverProvider {
12 fn hover(
16 &self,
17 _text: &Rope,
18 _offset: usize,
19 _window: &mut Window,
20 _cx: &mut App,
21 ) -> Task<Result<Option<lsp_types::Hover>>>;
22}
23
24impl InputBaseState<EditorMode> {
25 pub(super) fn handle_hover_popover(
27 &mut self,
28 offset: usize,
29 window: &mut Window,
30 cx: &mut Context<InputBaseState<EditorMode>>,
31 ) {
32 if self.selecting {
33 return;
34 }
35
36 let Some(provider) = self.extras.lsp.hover_provider.clone() else {
37 return;
38 };
39
40 if let Some(hover_popover) = self.extras.hover_popover.as_ref() {
41 if hover_popover.symbol_range.contains(&offset) {
42 return;
43 }
44 }
45
46 let text = self.text.clone();
47 let mut symbol_range = self.text.word_range(offset).unwrap_or(offset..offset);
48 let editor = cx.entity();
49 let should_delay = self.extras.hover_popover.is_none();
50 self.extras.lsp._hover_task = cx.spawn_in(window, async move |this, cx| {
53 if should_delay {
54 cx.background_executor()
55 .timer(Duration::from_millis(150))
56 .await;
57 }
58
59 let task = this.update_in(cx, |_, window, cx| {
60 provider.hover(&text, offset, window, cx)
61 })?;
62 let result = task.await?;
63
64 _ = editor.update(cx, |editor, cx| {
65 match result {
66 Some(hover) => {
67 if let Some(range) = hover.range {
68 let start = editor.text.position_to_offset(&range.start);
69 let end = editor.text.position_to_offset(&range.end);
70 symbol_range = start..end;
71 }
72 editor.extras.hover_popover = Some(HoverPopoverState {
73 symbol_range,
74 hover,
75 });
76 }
77 None => {
78 editor.extras.hover_popover = None;
79 }
80 }
81 cx.notify();
82 });
83
84 Ok(())
85 });
86 }
87
88 pub(crate) fn handle_mouse_move(
89 &mut self,
90 offset: usize,
91 event: &MouseMoveEvent,
92 window: &mut Window,
93 cx: &mut Context<Self>,
94 ) {
95 if event.modifiers.alt {
96 self.clear_hover_state(cx);
97 return;
98 }
99 if event.modifiers.secondary() {
100 self.handle_hover_definition(offset, window, cx);
101 } else {
102 if !self.extras.hover_definition.is_empty() {
105 self.extras.hover_definition.clear();
106 cx.notify();
107 }
108 self.handle_hover_popover(offset, window, cx);
109 }
110 }
111
112 pub fn clear_hover_state(&mut self, cx: &mut Context<Self>) {
113 let changed =
114 !self.extras.hover_definition.is_empty() || self.extras.hover_popover.is_some();
115 self.extras.hover_definition.clear();
116 self.extras.hover_popover = None;
117 self.extras.lsp._hover_task = Task::ready(Ok(()));
118 if changed {
119 cx.notify();
120 }
121 }
122}