Skip to main content

gpui_base/input/editor/
mod.rs

1use gpui::{App, Div, Entity, InteractiveElement as _, IntoElement, RenderOnce, Stateful, Window};
2
3use super::{EditorMode, InputBaseState, InputModeKind};
4
5/// State for source-code editing.
6///
7/// This is the shared editing engine in its code-editor kind. Languages, line
8/// numbers, folding, indent guides, diagnostics, decorations, and the LSP
9/// providers exist on this kind only, so an ordinary input or textarea never
10/// exposes them.
11pub type EditorState = InputBaseState<EditorMode>;
12
13impl InputModeKind for EditorMode {
14    const MULTI_LINE: bool = true;
15    const CODE_EDITOR: bool = true;
16
17    type Extras = super::EditorExtras;
18
19    fn hover_definition_style(
20        state: &InputBaseState<Self>,
21        _cx: &App,
22    ) -> Option<(std::ops::Range<usize>, gpui::HighlightStyle)> {
23        state.hover_definition_style()
24    }
25
26    fn hover_definition_hitbox(
27        state: &InputBaseState<Self>,
28        window: &mut Window,
29        _cx: &App,
30    ) -> Option<gpui::Hitbox> {
31        state.hover_definition_hitbox(window)
32    }
33
34    fn reset_language_features(state: &mut InputBaseState<Self>) {
35        state.extras.lsp.reset();
36    }
37
38    fn reset_annotations(state: &mut InputBaseState<Self>) {
39        state.extras.hover_popover = None;
40        state.extras.decorations.clear();
41    }
42
43    fn adjust_annotations(
44        state: &mut InputBaseState<Self>,
45        range: &std::ops::Range<usize>,
46        new_len: usize,
47    ) {
48        state.extras.decorations.adjust_for_edit(range, new_len);
49    }
50
51    fn refresh_language_features(
52        state: &mut InputBaseState<Self>,
53        window: &mut Window,
54        cx: &mut gpui::Context<InputBaseState<Self>>,
55    ) {
56        let text = state.text().clone();
57        state.extras.lsp.update(&text, window, cx);
58    }
59
60    fn accept_inline_completion(
61        state: &mut InputBaseState<Self>,
62        window: &mut Window,
63        cx: &mut gpui::Context<InputBaseState<Self>>,
64    ) -> bool {
65        state.accept_inline_completion(window, cx)
66    }
67
68    fn has_inline_completion(state: &InputBaseState<Self>) -> bool {
69        state.has_inline_completion()
70    }
71
72    fn on_click(
73        state: &mut InputBaseState<Self>,
74        event: &gpui::MouseDownEvent,
75        offset: usize,
76        window: &mut Window,
77        cx: &mut gpui::Context<InputBaseState<Self>>,
78    ) -> bool {
79        state.handle_click_hover_definition(event, offset, window, cx)
80    }
81
82    fn clear_hover_state(
83        state: &mut InputBaseState<Self>,
84        cx: &mut gpui::Context<InputBaseState<Self>>,
85    ) {
86        state.clear_hover_state(cx);
87    }
88
89    fn on_text_typed(
90        state: &mut InputBaseState<Self>,
91        range: &std::ops::Range<usize>,
92        text: &str,
93        window: &mut Window,
94        cx: &mut gpui::Context<InputBaseState<Self>>,
95    ) {
96        state.handle_completion_trigger(range, text, window, cx);
97    }
98
99    fn clear_inline_completion(
100        state: &mut InputBaseState<Self>,
101        cx: &mut gpui::Context<InputBaseState<Self>>,
102    ) {
103        state.clear_inline_completion(cx);
104    }
105
106    fn hide_context_menu(
107        state: &mut InputBaseState<Self>,
108        cx: &mut gpui::Context<InputBaseState<Self>>,
109    ) {
110        state.hide_context_menu(cx);
111    }
112
113    fn is_context_menu_open(state: &InputBaseState<Self>, cx: &App) -> bool {
114        state.is_context_menu_open(cx)
115    }
116
117    fn handle_context_menu_action(
118        state: &mut InputBaseState<Self>,
119        action: Box<dyn gpui::Action>,
120        window: &mut Window,
121        cx: &mut gpui::Context<InputBaseState<Self>>,
122    ) -> bool {
123        state.handle_action_for_context_menu(action, window, cx)
124    }
125
126    fn on_hover_definition(
127        state: &mut InputBaseState<Self>,
128        offset: usize,
129        window: &mut Window,
130        cx: &mut gpui::Context<InputBaseState<Self>>,
131    ) {
132        state.handle_hover_definition(offset, window, cx);
133    }
134
135    fn on_mouse_move(
136        state: &mut InputBaseState<Self>,
137        offset: usize,
138        event: &gpui::MouseMoveEvent,
139        window: &mut Window,
140        cx: &mut gpui::Context<InputBaseState<Self>>,
141    ) {
142        state.handle_mouse_move(offset, event, window, cx);
143    }
144
145    fn drive_highlighter(
146        highlighter: &std::rc::Rc<std::cell::RefCell<Option<Box<dyn super::InputHighlighter>>>>,
147        edit: super::InputEdit,
148        text: &ropey::Rope,
149        folding: bool,
150        window: &mut Window,
151        cx: &mut gpui::Context<InputBaseState<Self>>,
152    ) {
153        let mut highlighter = highlighter.borrow_mut();
154        let Some(highlighter) = highlighter.as_mut() else {
155            return;
156        };
157        highlighter.update(Some(edit), text, folding, window, cx);
158    }
159
160    fn register_actions(
161        element: Stateful<Div>,
162        entity: &Entity<InputBaseState<Self>>,
163        window: &mut Window,
164    ) -> Stateful<Div> {
165        element
166            .on_action(window.listener_for(entity, InputBaseState::on_action_toggle_code_actions))
167            .on_action(window.listener_for(entity, InputBaseState::on_action_go_to_definition))
168    }
169}
170
171impl EditorState {
172    /// The LSP providers and their cached results.
173    ///
174    /// This exists on the editor alone: an ordinary input or textarea has no
175    /// language server, and no field to reach one through.
176    pub fn lsp(&self) -> &super::Lsp {
177        &self.extras.lsp
178    }
179
180    /// The LSP providers, mutably. Configure the providers through this.
181    pub fn lsp_mut(&mut self) -> &mut super::Lsp {
182        &mut self.extras.lsp
183    }
184}
185
186/// An unstyled source-code editor.
187#[derive(IntoElement)]
188pub struct Editor {
189    state: Entity<EditorState>,
190}
191
192impl Editor {
193    pub fn new(state: &Entity<EditorState>) -> Self {
194        Self {
195            state: state.clone(),
196        }
197    }
198}
199
200impl RenderOnce for Editor {
201    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
202        self.state
203    }
204}
205
206/// What a code editor exposes to the renderer. See [`crate::input::InputExtras`].
207impl crate::input::InputExtras for super::EditorExtras {
208    fn decoration_layers(&self) -> Vec<&[super::TextDecoration]> {
209        self.decorations.iter().collect()
210    }
211
212    fn semantic_token_styles(
213        &self,
214        text: &ropey::Rope,
215        range: &std::ops::Range<usize>,
216        resolver: &dyn crate::input::HighlightStyleResolver,
217    ) -> Vec<(std::ops::Range<usize>, gpui::HighlightStyle)> {
218        self.lsp.semantic_tokens_for_range(text, range, resolver)
219    }
220
221    fn document_color_swatches(
222        &self,
223        text: &ropey::Rope,
224        range: &std::ops::Range<usize>,
225    ) -> Vec<(std::ops::Range<usize>, gpui::Hsla)> {
226        self.lsp.document_colors_for_range(text, range)
227    }
228
229    fn hover_symbol_range(&self) -> Option<std::ops::Range<usize>> {
230        self.hover_popover
231            .as_ref()
232            .map(|session| session.symbol_range.clone())
233    }
234
235    fn inline_completion_item(&self) -> Option<&lsp_types::InlineCompletionItem> {
236        self.inline_completion.item.as_ref()
237    }
238
239    fn context_menu_capabilities(&self) -> (bool, bool) {
240        (
241            self.lsp.definition_provider.is_some(),
242            !self.lsp.code_action_providers.is_empty(),
243        )
244    }
245}