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 task = provider.hover(&self.text, offset, window, cx);
48 let mut symbol_range = self.text.word_range(offset).unwrap_or(offset..offset);
49 let editor = cx.entity();
50 let should_delay = self.extras.hover_popover.is_none();
51 self.extras.lsp._hover_task = cx.spawn_in(window, async move |_, cx| {
52 if should_delay {
53 cx.background_executor()
54 .timer(Duration::from_millis(150))
55 .await;
56 }
57
58 let result = task.await?;
59
60 _ = editor.update(cx, |editor, cx| {
61 match result {
62 Some(hover) => {
63 if let Some(range) = hover.range {
64 let start = editor.text.position_to_offset(&range.start);
65 let end = editor.text.position_to_offset(&range.end);
66 symbol_range = start..end;
67 }
68 editor.extras.hover_popover = Some(HoverPopoverState {
69 symbol_range,
70 hover,
71 });
72 }
73 None => {
74 editor.extras.hover_popover = None;
75 }
76 }
77 cx.notify();
78 });
79
80 Ok(())
81 });
82 }
83
84 pub(crate) fn handle_mouse_move(
85 &mut self,
86 offset: usize,
87 event: &MouseMoveEvent,
88 window: &mut Window,
89 cx: &mut Context<Self>,
90 ) {
91 if event.modifiers.secondary() {
92 self.handle_hover_definition(offset, window, cx);
93 } else {
94 self.extras.hover_definition.clear();
95 self.handle_hover_popover(offset, window, cx);
96 }
97 cx.notify();
98 }
99
100 pub fn clear_hover_state(&mut self, cx: &mut Context<Self>) {
101 let changed =
102 !self.extras.hover_definition.is_empty() || self.extras.hover_popover.is_some();
103 self.extras.hover_definition.clear();
104 self.extras.hover_popover = None;
105 self.extras.lsp._hover_task = Task::ready(Ok(()));
106 if changed {
107 cx.notify();
108 }
109 }
110}