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        state.extras.range_decorations.clear();
42        state.extras.gutter_decorations.clear();
43        state.extras.overview_ruler_decorations.clear();
44    }
45
46    fn editing_syntax_context(state: &InputBaseState<Self>, offset: usize) -> super::SyntaxContext {
47        state.syntax_context_at(offset)
48    }
49
50    fn adjust_annotations(
51        state: &mut InputBaseState<Self>,
52        range: &std::ops::Range<usize>,
53        new_len: usize,
54    ) {
55        state.extras.decorations.adjust_for_edit(range, new_len);
56        state
57            .extras
58            .range_decorations
59            .adjust_for_edit(range, new_len);
60        state
61            .extras
62            .gutter_decorations
63            .adjust_for_edit(range, new_len);
64        state
65            .extras
66            .overview_ruler_decorations
67            .adjust_for_edit(range, new_len);
68    }
69
70    fn refresh_language_features(
71        state: &mut InputBaseState<Self>,
72        window: &mut Window,
73        cx: &mut gpui::Context<InputBaseState<Self>>,
74    ) {
75        let text = state.text().clone();
76        state.extras.lsp.update(&text, window, cx);
77    }
78
79    fn accept_inline_completion(
80        state: &mut InputBaseState<Self>,
81        window: &mut Window,
82        cx: &mut gpui::Context<InputBaseState<Self>>,
83    ) -> bool {
84        state.accept_inline_completion(window, cx)
85    }
86
87    fn has_inline_completion(state: &InputBaseState<Self>) -> bool {
88        state.has_inline_completion()
89    }
90
91    fn on_click(
92        state: &mut InputBaseState<Self>,
93        event: &gpui::MouseDownEvent,
94        offset: usize,
95        window: &mut Window,
96        cx: &mut gpui::Context<InputBaseState<Self>>,
97    ) -> bool {
98        state.handle_click_hover_definition(event, offset, window, cx)
99    }
100
101    fn clear_hover_state(
102        state: &mut InputBaseState<Self>,
103        cx: &mut gpui::Context<InputBaseState<Self>>,
104    ) {
105        state.clear_hover_state(cx);
106    }
107
108    fn on_text_typed(
109        state: &mut InputBaseState<Self>,
110        range: &std::ops::Range<usize>,
111        text: &str,
112        window: &mut Window,
113        cx: &mut gpui::Context<InputBaseState<Self>>,
114    ) {
115        state.handle_completion_trigger(range, text, window, cx);
116    }
117
118    fn clear_inline_completion(
119        state: &mut InputBaseState<Self>,
120        cx: &mut gpui::Context<InputBaseState<Self>>,
121    ) {
122        state.clear_inline_completion(cx);
123    }
124
125    fn hide_context_menu(
126        state: &mut InputBaseState<Self>,
127        cx: &mut gpui::Context<InputBaseState<Self>>,
128    ) {
129        state.hide_context_menu(cx);
130    }
131
132    fn is_context_menu_open(state: &InputBaseState<Self>, cx: &App) -> bool {
133        state.is_context_menu_open(cx)
134    }
135
136    fn handle_context_menu_action(
137        state: &mut InputBaseState<Self>,
138        action: Box<dyn gpui::Action>,
139        window: &mut Window,
140        cx: &mut gpui::Context<InputBaseState<Self>>,
141    ) -> bool {
142        state.handle_action_for_context_menu(action, window, cx)
143    }
144
145    fn on_hover_definition(
146        state: &mut InputBaseState<Self>,
147        offset: usize,
148        window: &mut Window,
149        cx: &mut gpui::Context<InputBaseState<Self>>,
150    ) {
151        state.handle_hover_definition(offset, window, cx);
152    }
153
154    fn on_mouse_move(
155        state: &mut InputBaseState<Self>,
156        offset: usize,
157        event: &gpui::MouseMoveEvent,
158        window: &mut Window,
159        cx: &mut gpui::Context<InputBaseState<Self>>,
160    ) {
161        state.handle_mouse_move(offset, event, window, cx);
162    }
163
164    fn drive_highlighter(
165        highlighter: &std::rc::Rc<std::cell::RefCell<Option<Box<dyn super::InputHighlighter>>>>,
166        edit: super::InputEdit,
167        text: &ropey::Rope,
168        folding: bool,
169        window: &mut Window,
170        cx: &mut gpui::Context<InputBaseState<Self>>,
171    ) {
172        let mut highlighter = highlighter.borrow_mut();
173        let Some(highlighter) = highlighter.as_mut() else {
174            return;
175        };
176        highlighter.update(Some(edit), text, folding, window, cx);
177    }
178
179    fn drive_highlighter_batch(
180        highlighter: &std::rc::Rc<std::cell::RefCell<Option<Box<dyn super::InputHighlighter>>>>,
181        edits: &[(super::InputEdit, ropey::Rope)],
182        folding: bool,
183        window: &mut Window,
184        cx: &mut gpui::Context<InputBaseState<Self>>,
185    ) {
186        let mut highlighter = highlighter.borrow_mut();
187        let Some(highlighter) = highlighter.as_mut() else {
188            return;
189        };
190        highlighter.update_batch(edits, folding, window, cx);
191    }
192
193    fn register_actions(
194        element: Stateful<Div>,
195        entity: &Entity<InputBaseState<Self>>,
196        window: &mut Window,
197    ) -> Stateful<Div> {
198        element
199            .key_context("Input mode=editor")
200            .on_action(window.listener_for(entity, InputBaseState::on_action_toggle_code_actions))
201            .on_action(window.listener_for(entity, InputBaseState::on_action_show_completions))
202            .on_action(window.listener_for(entity, InputBaseState::on_action_go_to_definition))
203    }
204}
205
206impl EditorState {
207    /// The LSP providers and their cached results.
208    ///
209    /// This exists on the editor alone: an ordinary input or textarea has no
210    /// language server, and no field to reach one through.
211    pub fn lsp(&self) -> &super::Lsp {
212        &self.extras.lsp
213    }
214
215    /// The LSP providers, mutably. Configure the providers through this.
216    pub fn lsp_mut(&mut self) -> &mut super::Lsp {
217        &mut self.extras.lsp
218    }
219
220    /// Syntax context at `offset`, or `Code` when the language has no provider.
221    pub(crate) fn syntax_context_at(&self, offset: usize) -> super::SyntaxContext {
222        self.mode.syntax_context_at(&self.text, offset)
223    }
224}
225
226/// An unstyled source-code editor.
227#[derive(IntoElement)]
228pub struct Editor {
229    state: Entity<EditorState>,
230}
231
232impl Editor {
233    pub fn new(state: &Entity<EditorState>) -> Self {
234        Self {
235            state: state.clone(),
236        }
237    }
238}
239
240impl RenderOnce for Editor {
241    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
242        self.state
243    }
244}
245
246/// What a code editor exposes to the renderer. See [`crate::input::InputExtras`].
247impl crate::input::InputExtras for super::EditorExtras {
248    fn decoration_layers(&self) -> Vec<&[super::TextDecoration]> {
249        self.decorations.iter().collect()
250    }
251
252    fn range_decorations(&self, ranges: &[std::ops::Range<usize>]) -> Vec<&super::RangeDecoration> {
253        self.range_decorations.intersecting(ranges)
254    }
255
256    fn gutter_decorations_width(&self) -> Option<gpui::Pixels> {
257        self.gutter_decorations
258            .has_collections()
259            .then_some(self.gutter_decorations_width)
260    }
261
262    fn gutter_decorations(&self, range: std::ops::Range<usize>) -> Vec<&super::GutterDecoration> {
263        self.gutter_decorations.intersecting(&[range])
264    }
265
266    fn overview_ruler_decorations(&self) -> Vec<&super::OverviewRulerDecoration> {
267        self.overview_ruler_decorations.iter().flatten().collect()
268    }
269
270    fn overview_ruler_revision(&self) -> u64 {
271        self.overview_ruler_decorations.revision()
272    }
273
274    fn semantic_token_styles(
275        &self,
276        text: &ropey::Rope,
277        range: &std::ops::Range<usize>,
278        resolver: &dyn crate::input::HighlightStyleResolver,
279    ) -> Vec<(std::ops::Range<usize>, gpui::HighlightStyle)> {
280        self.lsp.semantic_tokens_for_range(text, range, resolver)
281    }
282
283    fn document_color_swatches(
284        &self,
285        text: &ropey::Rope,
286        range: &std::ops::Range<usize>,
287    ) -> Vec<(std::ops::Range<usize>, gpui::Hsla)> {
288        self.lsp.document_colors_for_range(text, range)
289    }
290
291    fn hover_symbol_range(&self) -> Option<std::ops::Range<usize>> {
292        self.hover_popover
293            .as_ref()
294            .map(|session| session.symbol_range.clone())
295    }
296
297    fn inline_completion_item(&self) -> Option<&lsp_types::InlineCompletionItem> {
298        self.inline_completion.item.as_ref()
299    }
300
301    fn context_menu_capabilities(&self) -> (bool, bool) {
302        (
303            self.lsp.definition_provider.is_some(),
304            !self.lsp.code_action_providers.is_empty(),
305        )
306    }
307}