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