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 .key_context("Input mode=editor")
190 .on_action(window.listener_for(entity, InputBaseState::on_action_toggle_code_actions))
191 .on_action(window.listener_for(entity, InputBaseState::on_action_show_completions))
192 .on_action(window.listener_for(entity, InputBaseState::on_action_go_to_definition))
193 }
194}
195
196impl EditorState {
197 pub fn lsp(&self) -> &super::Lsp {
202 &self.extras.lsp
203 }
204
205 pub fn lsp_mut(&mut self) -> &mut super::Lsp {
207 &mut self.extras.lsp
208 }
209
210 pub(crate) fn syntax_context_at(&self, offset: usize) -> super::SyntaxContext {
212 self.mode.syntax_context_at(&self.text, offset)
213 }
214}
215
216#[derive(IntoElement)]
218pub struct Editor {
219 state: Entity<EditorState>,
220}
221
222impl Editor {
223 pub fn new(state: &Entity<EditorState>) -> Self {
224 Self {
225 state: state.clone(),
226 }
227 }
228}
229
230impl RenderOnce for Editor {
231 fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
232 self.state
233 }
234}
235
236impl crate::input::InputExtras for super::EditorExtras {
238 fn decoration_layers(&self) -> Vec<&[super::TextDecoration]> {
239 self.decorations.iter().collect()
240 }
241
242 fn range_decorations(&self, ranges: &[std::ops::Range<usize>]) -> Vec<&super::RangeDecoration> {
243 self.range_decorations.intersecting(ranges)
244 }
245
246 fn semantic_token_styles(
247 &self,
248 text: &ropey::Rope,
249 range: &std::ops::Range<usize>,
250 resolver: &dyn crate::input::HighlightStyleResolver,
251 ) -> Vec<(std::ops::Range<usize>, gpui::HighlightStyle)> {
252 self.lsp.semantic_tokens_for_range(text, range, resolver)
253 }
254
255 fn document_color_swatches(
256 &self,
257 text: &ropey::Rope,
258 range: &std::ops::Range<usize>,
259 ) -> Vec<(std::ops::Range<usize>, gpui::Hsla)> {
260 self.lsp.document_colors_for_range(text, range)
261 }
262
263 fn hover_symbol_range(&self) -> Option<std::ops::Range<usize>> {
264 self.hover_popover
265 .as_ref()
266 .map(|session| session.symbol_range.clone())
267 }
268
269 fn inline_completion_item(&self) -> Option<&lsp_types::InlineCompletionItem> {
270 self.inline_completion.item.as_ref()
271 }
272
273 fn context_menu_capabilities(&self) -> (bool, bool) {
274 (
275 self.lsp.definition_provider.is_some(),
276 !self.lsp.code_action_providers.is_empty(),
277 )
278 }
279}