gpui_component/input/lsp/
hover.rs

1use 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
9/// Hover provider
10///
11/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover
12pub trait HoverProvider {
13    /// textDocument/hover
14    ///
15    /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover
16    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    /// Handle hover trigger LSP request.
27    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        // Currently not implemented.
48        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}