gpui_component/input/lsp/
hover.rs

1use anyhow::Result;
2use gpui::{App, Context, Task, Window};
3use ropey::Rope;
4
5use crate::input::{popovers::HoverPopover, InputState, RopeExt};
6
7/// Hover provider
8///
9/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover
10pub trait HoverProvider {
11    /// textDocument/hover
12    ///
13    /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover
14    fn hover(
15        &self,
16        _text: &Rope,
17        _offset: usize,
18        _window: &mut Window,
19        _cx: &mut App,
20    ) -> Task<Result<Option<lsp_types::Hover>>>;
21}
22
23impl InputState {
24    /// Handle hover trigger LSP request.
25    pub(super) fn handle_hover_popover(
26        &mut self,
27        offset: usize,
28        window: &mut Window,
29        cx: &mut Context<InputState>,
30    ) {
31        if self.selecting {
32            return;
33        }
34
35        let Some(provider) = self.lsp.hover_provider.clone() else {
36            return;
37        };
38
39        if let Some(hover_popover) = self.hover_popover.as_ref() {
40            if hover_popover.read(cx).is_same(offset) {
41                return;
42            }
43        }
44
45        // Currently not implemented.
46        let task = provider.hover(&self.text, offset, window, cx);
47        let mut symbol_range = self.text.word_range(offset).unwrap_or(offset..offset);
48        let editor = cx.entity();
49        self.lsp._hover_task = cx.spawn_in(window, async move |_, cx| {
50            let result = task.await?;
51
52            _ = editor.update(cx, |editor, cx| match result {
53                Some(hover) => {
54                    if let Some(range) = hover.range {
55                        let start = editor.text.position_to_offset(&range.start);
56                        let end = editor.text.position_to_offset(&range.end);
57                        symbol_range = start..end;
58                    }
59                    let hover_popover = HoverPopover::new(cx.entity(), symbol_range, &hover, cx);
60                    editor.hover_popover = Some(hover_popover);
61                }
62                None => {
63                    editor.hover_popover = None;
64                }
65            });
66
67            Ok(())
68        });
69    }
70}