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        self.lsp._hover_task = cx.spawn_in(window, async move |_, cx| {
52            cx.background_executor()
53                .timer(Duration::from_millis(150))
54                .await;
55            let result = task.await?;
56
57            _ = editor.update(cx, |editor, cx| match result {
58                Some(hover) => {
59                    if let Some(range) = hover.range {
60                        let start = editor.text.position_to_offset(&range.start);
61                        let end = editor.text.position_to_offset(&range.end);
62                        symbol_range = start..end;
63                    }
64                    let hover_popover = HoverPopover::new(cx.entity(), symbol_range, &hover, cx);
65                    editor.hover_popover = Some(hover_popover);
66                }
67                None => {
68                    editor.hover_popover = None;
69                }
70            });
71
72            Ok(())
73        });
74    }
75}