gpui_component/input/lsp/
mod.rs

1use anyhow::Result;
2use gpui::{App, Context, Hsla, MouseMoveEvent, Task, Window};
3use ropey::Rope;
4use std::rc::Rc;
5
6use crate::input::{popovers::ContextMenu, InputState, RopeExt};
7
8mod code_actions;
9mod completions;
10mod definitions;
11mod document_colors;
12mod hover;
13
14pub use code_actions::*;
15pub use completions::*;
16pub use definitions::*;
17pub use document_colors::*;
18pub use hover::*;
19
20/// LSP ServerCapabilities
21///
22/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#serverCapabilities
23pub struct Lsp {
24    /// The completion provider.
25    pub completion_provider: Option<Rc<dyn CompletionProvider>>,
26    /// The code action providers.
27    pub code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
28    /// The hover provider.
29    pub hover_provider: Option<Rc<dyn HoverProvider>>,
30    /// The definition provider.
31    pub definition_provider: Option<Rc<dyn DefinitionProvider>>,
32    /// The document color provider.
33    pub document_color_provider: Option<Rc<dyn DocumentColorProvider>>,
34
35    document_colors: Vec<(lsp_types::Range, Hsla)>,
36    _hover_task: Task<Result<()>>,
37    _document_color_task: Task<Result<()>>,
38}
39
40impl Default for Lsp {
41    fn default() -> Self {
42        Self {
43            completion_provider: None,
44            code_action_providers: vec![],
45            hover_provider: None,
46            definition_provider: None,
47            document_color_provider: None,
48            document_colors: vec![],
49            _hover_task: Task::ready(Ok(())),
50            _document_color_task: Task::ready(Ok(())),
51        }
52    }
53}
54
55impl Lsp {
56    /// Update the LSP when the text changes.
57    pub(crate) fn update(
58        &mut self,
59        text: &Rope,
60        window: &mut Window,
61        cx: &mut Context<InputState>,
62    ) {
63        self.update_document_colors(text, window, cx);
64    }
65
66    /// Reset all LSP states.
67    pub(crate) fn reset(&mut self) {
68        self.document_colors.clear();
69        self._hover_task = Task::ready(Ok(()));
70        self._document_color_task = Task::ready(Ok(()));
71    }
72}
73
74impl InputState {
75    pub(crate) fn hide_context_menu(&mut self, cx: &mut Context<Self>) {
76        self.context_menu = None;
77        self._context_menu_task = Task::ready(Ok(()));
78        cx.notify();
79    }
80
81    pub(crate) fn is_context_menu_open(&self, cx: &App) -> bool {
82        let Some(menu) = self.context_menu.as_ref() else {
83            return false;
84        };
85
86        menu.is_open(cx)
87    }
88
89    /// Handles an action for the completion menu, if it exists.
90    ///
91    /// Return true if the action was handled, otherwise false.
92    pub fn handle_action_for_context_menu(
93        &mut self,
94        action: Box<dyn gpui::Action>,
95        window: &mut Window,
96        cx: &mut Context<Self>,
97    ) -> bool {
98        let Some(menu) = self.context_menu.as_ref() else {
99            return false;
100        };
101
102        let mut handled = false;
103
104        match menu {
105            ContextMenu::Completion(menu) => {
106                _ = menu.update(cx, |menu, cx| {
107                    handled = menu.handle_action(action, window, cx)
108                });
109            }
110            ContextMenu::CodeAction(menu) => {
111                _ = menu.update(cx, |menu, cx| {
112                    handled = menu.handle_action(action, window, cx)
113                });
114            }
115            ContextMenu::MouseContext(..) => {}
116        };
117
118        handled
119    }
120
121    /// Apply a list of [`lsp_types::TextEdit`] to mutate the text.
122    pub fn apply_lsp_edits(
123        &mut self,
124        text_edits: &Vec<lsp_types::TextEdit>,
125        window: &mut Window,
126        cx: &mut Context<Self>,
127    ) {
128        for edit in text_edits {
129            let start = self.text.position_to_offset(&edit.range.start);
130            let end = self.text.position_to_offset(&edit.range.end);
131
132            let range_utf16 = self.range_to_utf16(&(start..end));
133            self.replace_text_in_range_silent(Some(range_utf16), &edit.new_text, window, cx);
134        }
135    }
136
137    pub(super) fn handle_mouse_move(
138        &mut self,
139        offset: usize,
140        event: &MouseMoveEvent,
141        window: &mut Window,
142        cx: &mut Context<InputState>,
143    ) {
144        if event.modifiers.secondary() {
145            self.handle_hover_definition(offset, window, cx);
146        } else {
147            self.hover_definition.clear();
148            self.handle_hover_popover(offset, window, cx);
149        }
150        cx.notify();
151    }
152}