1use gpui::{App, Div, Entity, InteractiveElement as _, IntoElement, RenderOnce, Stateful, Window};
2
3use super::{EditorMode, InputBaseState, InputModeKind};
4
5pub 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 pub fn lsp(&self) -> &super::Lsp {
200 &self.extras.lsp
201 }
202
203 pub fn lsp_mut(&mut self) -> &mut super::Lsp {
205 &mut self.extras.lsp
206 }
207
208 pub(crate) fn syntax_context_at(&self, offset: usize) -> super::SyntaxContext {
210 self.mode.syntax_context_at(&self.text, offset)
211 }
212}
213
214#[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
234impl 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}