Skip to main content

gpui_base/input/editor/lsp/
mod.rs

1use anyhow::Result;
2use gpui::{App, Context, Hsla, SharedString, Task, Window};
3use ropey::Rope;
4use std::rc::Rc;
5
6use crate::input::{EditorMode, InputBaseState, RopeExt};
7
8mod code_actions;
9mod completions;
10mod definitions;
11mod document_colors;
12mod hover;
13mod overlay;
14mod semantic_tokens;
15
16pub use code_actions::*;
17pub use completions::*;
18pub use definitions::*;
19pub use document_colors::*;
20pub use hover::*;
21pub use overlay::*;
22pub use semantic_tokens::*;
23
24/// Host hook to show a document when following an LSP location
25/// (Go to Definition), modeled after the `window/showDocument` request.
26///
27/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showDocument
28///
29/// Called before the built-in behavior. Return `true` if the host has shown
30/// the document (e.g. opened a docs window for a virtual/external URI);
31/// return `false` to fall through to the default handling (`external` URIs
32/// open in the browser, anything else jumps within the current document).
33pub type ShowDocumentHandler =
34    Rc<dyn Fn(&lsp_types::ShowDocumentParams, &mut Window, &mut App) -> bool>;
35
36/// LSP ServerCapabilities
37///
38/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#serverCapabilities
39pub struct Lsp {
40    /// The completion provider.
41    pub completion_provider: Option<Rc<dyn CompletionProvider>>,
42    /// The code action providers.
43    pub code_action_providers: Vec<Rc<dyn CodeActionProvider>>,
44    /// The hover provider.
45    pub hover_provider: Option<Rc<dyn HoverProvider>>,
46    /// The definition provider.
47    pub definition_provider: Option<Rc<dyn DefinitionProvider>>,
48    /// The document color provider.
49    pub document_color_provider: Option<Rc<dyn DocumentColorProvider>>,
50    /// The range semantic tokens provider.
51    pub semantic_tokens_provider: Option<Rc<dyn DocumentRangeSemanticTokensProvider>>,
52    /// Optional host hook to show documents for Go to Definition locations,
53    /// following the `window/showDocument` request (see [`ShowDocumentHandler`]).
54    ///
55    /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#window_showDocument
56    pub show_document: Option<ShowDocumentHandler>,
57
58    /// Display options for the completion popover.
59    pub completion_menu: CompletionMenuOptions,
60
61    /// Decides which bytes of each completion label the menu paints.
62    ///
63    /// Defaults to [`highlight_prefix`]. Replace it to paint a contiguous
64    /// match, a character-by-character match, or ranges the host computed.
65    completion_highlighter: Rc<CompletionHighlighter>,
66
67    pub(crate) document_colors: Vec<(lsp_types::Range, Hsla)>,
68    /// Cached semantic tokens as absolute position ranges + theme token-type
69    /// names. Color is resolved from the name at paint time so theme switches
70    /// take effect without a refetch.
71    pub(crate) semantic_tokens: Vec<(lsp_types::Range, SharedString)>,
72    pub(crate) _hover_task: Task<Result<()>>,
73    pub(crate) _definition_task: Task<Result<()>>,
74    pub(crate) _document_color_task: Task<()>,
75    pub(crate) _semantic_tokens_task: Task<()>,
76}
77
78impl Default for Lsp {
79    fn default() -> Self {
80        Self {
81            completion_provider: None,
82            code_action_providers: vec![],
83            hover_provider: None,
84            definition_provider: None,
85            document_color_provider: None,
86            completion_menu: CompletionMenuOptions::default(),
87            completion_highlighter: Rc::new(highlight_prefix),
88            semantic_tokens_provider: None,
89            show_document: None,
90            document_colors: vec![],
91            semantic_tokens: vec![],
92            _hover_task: Task::ready(Ok(())),
93            _definition_task: Task::ready(Ok(())),
94            _document_color_task: Task::ready(()),
95            _semantic_tokens_task: Task::ready(()),
96        }
97    }
98}
99
100impl Lsp {
101    /// Set the completion label highlighter when constructing the LSP configuration.
102    pub fn with_completion_highlighter(mut self, highlighter: Rc<CompletionHighlighter>) -> Self {
103        self.set_completion_highlighter(highlighter);
104        self
105    }
106
107    /// Return the completion label highlighter.
108    pub fn completion_highlighter(&self) -> &Rc<CompletionHighlighter> {
109        &self.completion_highlighter
110    }
111
112    /// Replace the completion label highlighter.
113    ///
114    /// Notify the editor after updating its LSP configuration to repaint an open menu.
115    pub fn set_completion_highlighter(&mut self, highlighter: Rc<CompletionHighlighter>) {
116        self.completion_highlighter = highlighter;
117    }
118
119    /// Update the LSP when the text changes.
120    pub(crate) fn update(
121        &mut self,
122        text: &Rope,
123        window: &mut Window,
124        cx: &mut Context<InputBaseState<EditorMode>>,
125    ) {
126        self.update_document_colors(text, window, cx);
127        self.update_semantic_tokens(text, window, cx);
128    }
129
130    /// Reset all LSP states.
131    pub(crate) fn reset(&mut self) {
132        self.document_colors.clear();
133        self.semantic_tokens.clear();
134        self._hover_task = Task::ready(Ok(()));
135        self._definition_task = Task::ready(Ok(()));
136        self._document_color_task = Task::ready(());
137        self._semantic_tokens_task = Task::ready(());
138    }
139}
140
141impl InputBaseState<EditorMode> {
142    /// Apply a list of [`lsp_types::TextEdit`] to mutate the text.
143    pub fn apply_lsp_edits(
144        &mut self,
145        text_edits: &Vec<lsp_types::TextEdit>,
146        window: &mut Window,
147        cx: &mut Context<Self>,
148    ) {
149        for edit in text_edits {
150            let start = self.text.position_to_offset(&edit.range.start);
151            let end = self.text.position_to_offset(&edit.range.end);
152
153            let range_utf16 = self.range_to_utf16(&(start..end));
154            self.replace_text_in_range_silent(Some(range_utf16), &edit.new_text, window, cx);
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162
163    #[test]
164    fn test_lsp_builder() {
165        let highlighter: Rc<CompletionHighlighter> = Rc::new(highlight_contains);
166        let mut lsp = Lsp::default().with_completion_highlighter(highlighter.clone());
167        assert!(Rc::ptr_eq(lsp.completion_highlighter(), &highlighter));
168
169        let replacement: Rc<CompletionHighlighter> = Rc::new(highlight_characters);
170        lsp.set_completion_highlighter(replacement.clone());
171        assert!(Rc::ptr_eq(lsp.completion_highlighter(), &replacement));
172    }
173}