gpui_component/input/lsp/
hover.rs1use std::time::Duration;
2
3use anyhow::Result;
4use gpui::{App, Context, Task, Window};
5use ropey::Rope;
6
7use crate::input::{popovers::HoverPopover, InputState, RopeExt};
8
9pub trait HoverProvider {
13 fn hover(
17 &self,
18 _text: &Rope,
19 _offset: usize,
20 _window: &mut Window,
21 _cx: &mut App,
22 ) -> Task<Result<Option<lsp_types::Hover>>>;
23}
24
25impl InputState {
26 pub(super) fn handle_hover_popover(
28 &mut self,
29 offset: usize,
30 window: &mut Window,
31 cx: &mut Context<InputState>,
32 ) {
33 if self.selecting {
34 return;
35 }
36
37 let Some(provider) = self.lsp.hover_provider.clone() else {
38 return;
39 };
40
41 if let Some(hover_popover) = self.hover_popover.as_ref() {
42 if hover_popover.read(cx).is_same(offset) {
43 return;
44 }
45 }
46
47 let task = provider.hover(&self.text, offset, window, cx);
49 let mut symbol_range = self.text.word_range(offset).unwrap_or(offset..offset);
50 let editor = cx.entity();
51 let should_delay = self.hover_popover.is_none();
52 self.lsp._hover_task = cx.spawn_in(window, async move |_, cx| {
53 if should_delay {
54 cx.background_executor()
55 .timer(Duration::from_millis(150))
56 .await;
57 }
58
59 let result = task.await?;
60
61 _ = editor.update(cx, |editor, cx| 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 let hover_popover = HoverPopover::new(cx.entity(), symbol_range, &hover, cx);
69 editor.hover_popover = Some(hover_popover);
70 }
71 None => {
72 editor.hover_popover = None;
73 }
74 });
75
76 Ok(())
77 });
78 }
79}