1use std::{
2 borrow::Cow,
3 fmt::Display,
4 ops::{
5 Mul,
6 Range,
7 },
8 time::Duration,
9};
10
11use freya_core::{
12 elements::paragraph::ParagraphHolderInner,
13 prelude::*,
14};
15use freya_edit::*;
16use ropey::Rope;
17use tree_sitter::InputEdit;
18
19use crate::{
20 editor_theme::EditorSyntaxTheme,
21 languages::EditorLanguage,
22 metrics::EditorMetrics,
23 syntax::InputEditExt,
24};
25
26pub struct CodeEditorData {
27 pub(crate) history: EditorHistory,
28 pub rope: Rope,
29 pub(crate) selection: TextSelection,
30 pub(crate) last_saved_history_change: usize,
31 pub(crate) metrics: EditorMetrics,
32 pub(crate) dragging: TextDragging,
33 pub(crate) scrolls: (i32, i32),
34 pub(crate) pending_edit: Option<InputEdit>,
35 pub language: Option<EditorLanguage>,
36 theme: EditorSyntaxTheme,
37}
38
39impl CodeEditorData {
40 pub fn new(rope: Rope, language: impl Into<Option<EditorLanguage>>) -> Self {
41 let mut data = Self {
42 rope,
43 selection: TextSelection::new_cursor(0),
44 history: EditorHistory::new(Duration::from_secs(1)),
45 last_saved_history_change: 0,
46 metrics: EditorMetrics::new(),
47 dragging: TextDragging::default(),
48 scrolls: (0, 0),
49 pending_edit: None,
50 language: language.into(),
51 theme: EditorSyntaxTheme::default(),
52 };
53 data.configure_highlighter();
54 data
55 }
56
57 fn configure_highlighter(&mut self) {
59 self.metrics
60 .highlighter
61 .set_language(self.language.as_ref(), &self.theme);
62 }
63
64 pub fn set_language(&mut self, language: impl Into<Option<EditorLanguage>>) {
66 self.language = language.into();
67 self.configure_highlighter();
68 }
69
70 pub fn is_edited(&self) -> bool {
71 self.history.current_change() != self.last_saved_history_change
72 }
73
74 pub fn mark_as_saved(&mut self) {
75 self.last_saved_history_change = self.history.current_change();
76 }
77
78 pub fn parse(&mut self) {
79 let edit = self.pending_edit.take();
80 self.metrics.run_parser(&self.rope, edit, &self.theme);
81 }
82
83 pub fn measure(&mut self, font_size: f32, font_family: &str) {
84 self.metrics
85 .measure_longest_line(font_size, font_family, &self.rope);
86 }
87
88 pub fn set_theme(&mut self, theme: EditorSyntaxTheme) {
89 self.theme = theme;
90 self.configure_highlighter();
91 }
92
93 pub fn process(
94 &mut self,
95 font_size: f32,
96 font_family: &str,
97 edit_event: EditableEvent,
98 ) -> bool {
99 let mut processed = false;
100 match edit_event {
101 EditableEvent::Down {
102 location,
103 editor_line,
104 holder,
105 } => {
106 let holder = holder.0.borrow();
107 let ParagraphHolderInner {
108 paragraph,
109 scale_factor,
110 } = holder.as_ref().unwrap();
111
112 let current_selection = self.selection().clone();
113
114 if self.dragging.shift || self.dragging.clicked {
115 self.selection_mut().set_as_range();
116 } else {
117 self.clear_selection();
118 }
119
120 if ¤t_selection != self.selection() {
121 processed = true;
122 }
123
124 self.dragging.clicked = true;
125
126 let char_position = paragraph.get_glyph_position_at_coordinate(
127 location.mul(*scale_factor).to_i32().to_tuple(),
128 );
129 let press_selection =
130 self.measure_selection(char_position.position as usize, editor_line);
131
132 let new_selection = match EventsCombos::pressed(location) {
133 PressEventType::Quadruple => {
134 TextSelection::new_range((0, self.rope.len_utf16_cu()))
135 }
136 PressEventType::Triple => {
137 let line = self.char_to_line(press_selection.pos());
138 let line_char = self.line_to_char(line);
139 let line_len = self.line(line).unwrap().utf16_len();
140 TextSelection::new_range((line_char, line_char + line_len))
141 }
142 PressEventType::Double => {
143 let range = self.find_word_boundaries(press_selection.pos());
144 TextSelection::new_range(range)
145 }
146 PressEventType::Single => press_selection,
147 };
148
149 if *self.selection() != new_selection {
150 *self.selection_mut() = new_selection;
151 processed = true;
152 }
153 }
154 EditableEvent::Move {
155 location,
156 editor_line,
157 holder,
158 } => {
159 if self.dragging.clicked {
160 let paragraph = holder.0.borrow();
161 let ParagraphHolderInner {
162 paragraph,
163 scale_factor,
164 } = paragraph.as_ref().unwrap();
165
166 let dist_position = location.mul(*scale_factor);
167
168 let dist_char = paragraph
170 .get_glyph_position_at_coordinate(dist_position.to_i32().to_tuple());
171 let to = dist_char.position as usize;
172
173 if self.get_selection().is_none() {
174 self.selection_mut().set_as_range();
175 processed = true;
176 }
177
178 let current_selection = self.selection().clone();
179
180 let new_selection = self.measure_selection(to, editor_line);
181
182 if current_selection != new_selection {
184 *self.selection_mut() = new_selection;
185 processed = true;
186 }
187 }
188 }
189 EditableEvent::Release => {
190 self.dragging.clicked = false;
191 }
192 EditableEvent::KeyDown {
193 key,
194 modifiers,
195 editor_line,
196 holder,
197 } => {
198 match key {
199 Key::Named(NamedKey::Shift) => {
201 self.dragging.shift = true;
202 }
203 _ => {
205 let event = self.process_key(
206 key,
207 &modifiers,
208 editor_line,
209 holder,
210 true,
211 true,
212 true,
213 true,
214 );
215 if event.contains(TextEvent::TEXT_CHANGED) {
216 self.parse();
217 self.measure(font_size, font_family);
218 self.dragging = TextDragging::default();
219 }
220 if !event.is_empty() {
221 processed = true;
222 }
223 }
224 }
225 }
226 EditableEvent::KeyUp { key, .. } => {
227 if *key == Key::Named(NamedKey::Shift) {
228 self.dragging.shift = false;
229 }
230 }
231 };
232 processed
233 }
234}
235
236impl Display for CodeEditorData {
237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238 f.write_str(&self.rope.to_string())
239 }
240}
241
242impl TextEditor for CodeEditorData {
243 type LinesIterator<'a>
244 = LinesIterator<'a>
245 where
246 Self: 'a;
247
248 fn lines(&self) -> Self::LinesIterator<'_> {
249 unimplemented!("Unused.")
250 }
251
252 fn text(&self) -> Cow<'_, str> {
253 self.rope.slice(..).into()
254 }
255
256 fn insert_char(&mut self, ch: char, idx: usize) -> usize {
257 let idx_utf8 = self.utf16_cu_to_char(idx);
258 let selection = self.selection.clone();
259
260 let start_byte = self.rope.char_to_byte(idx_utf8);
262 let start_line = self.rope.char_to_line(idx_utf8);
263 let start_line_byte = self.rope.line_to_byte(start_line);
264 let start_col = start_byte - start_line_byte;
265
266 let len_before_insert = self.rope.len_utf16_cu();
267 self.rope.insert_char(idx_utf8, ch);
268 let len_after_insert = self.rope.len_utf16_cu();
269
270 let inserted_text_len = len_after_insert - len_before_insert;
271
272 let new_end_char = idx_utf8 + 1; let new_end_byte = self.rope.char_to_byte(new_end_char);
275 let new_end_line = self.rope.char_to_line(new_end_char);
276 let new_end_line_byte = self.rope.line_to_byte(new_end_line);
277 let new_end_col = new_end_byte - new_end_line_byte;
278
279 self.pending_edit = Some(InputEdit::new_edit(
280 start_byte,
281 start_byte,
282 new_end_byte,
283 (start_line, start_col),
284 (start_line, start_col),
285 (new_end_line, new_end_col),
286 ));
287
288 self.history.push_change(HistoryChange::InsertChar {
289 idx,
290 ch,
291 len: inserted_text_len,
292 selection,
293 });
294
295 inserted_text_len
296 }
297
298 fn insert(&mut self, text: &str, idx: usize) -> usize {
299 let idx_utf8 = self.utf16_cu_to_char(idx);
300 let selection = self.selection.clone();
301
302 let start_byte = self.rope.char_to_byte(idx_utf8);
304 let start_line = self.rope.char_to_line(idx_utf8);
305 let start_line_byte = self.rope.line_to_byte(start_line);
306 let start_col = start_byte - start_line_byte;
307
308 let len_before_insert = self.rope.len_utf16_cu();
309 self.rope.insert(idx_utf8, text);
310 let len_after_insert = self.rope.len_utf16_cu();
311
312 let inserted_text_len = len_after_insert - len_before_insert;
313
314 let inserted_chars = text.chars().count();
316 let new_end_char = idx_utf8 + inserted_chars;
317 let new_end_byte = self.rope.char_to_byte(new_end_char);
318 let new_end_line = self.rope.char_to_line(new_end_char);
319 let new_end_line_byte = self.rope.line_to_byte(new_end_line);
320 let new_end_col = new_end_byte - new_end_line_byte;
321
322 self.pending_edit = Some(InputEdit::new_edit(
323 start_byte,
324 start_byte,
325 new_end_byte,
326 (start_line, start_col),
327 (start_line, start_col),
328 (new_end_line, new_end_col),
329 ));
330
331 self.history.push_change(HistoryChange::InsertText {
332 idx,
333 text: text.to_owned(),
334 len: inserted_text_len,
335 selection,
336 });
337
338 inserted_text_len
339 }
340
341 fn remove(&mut self, range_utf16: Range<usize>) -> usize {
342 let range =
343 self.utf16_cu_to_char(range_utf16.start)..self.utf16_cu_to_char(range_utf16.end);
344 let text = self.rope.slice(range.clone()).to_string();
345 let selection = self.selection.clone();
346
347 let start_byte = self.rope.char_to_byte(range.start);
349 let old_end_byte = self.rope.char_to_byte(range.end);
350 let start_line = self.rope.char_to_line(range.start);
351 let start_line_byte = self.rope.line_to_byte(start_line);
352 let start_col = start_byte - start_line_byte;
353 let old_end_line = self.rope.char_to_line(range.end);
354 let old_end_line_byte = self.rope.line_to_byte(old_end_line);
355 let old_end_col = old_end_byte - old_end_line_byte;
356
357 let len_before_remove = self.rope.len_utf16_cu();
358 self.rope.remove(range);
359 let len_after_remove = self.rope.len_utf16_cu();
360
361 let removed_text_len = len_before_remove - len_after_remove;
362
363 self.pending_edit = Some(InputEdit::new_edit(
365 start_byte,
366 old_end_byte,
367 start_byte,
368 (start_line, start_col),
369 (old_end_line, old_end_col),
370 (start_line, start_col),
371 ));
372
373 self.history.push_change(HistoryChange::Remove {
374 idx: range_utf16.end - removed_text_len,
375 text,
376 len: removed_text_len,
377 selection,
378 });
379
380 removed_text_len
381 }
382
383 fn char_to_line(&self, char_idx: usize) -> usize {
384 self.rope.char_to_line(char_idx)
385 }
386
387 fn line_to_char(&self, line_idx: usize) -> usize {
388 self.rope.line_to_char(line_idx)
389 }
390
391 fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
392 self.rope.utf16_cu_to_char(utf16_cu_idx)
393 }
394
395 fn char_to_utf16_cu(&self, idx: usize) -> usize {
396 self.rope.char_to_utf16_cu(idx)
397 }
398
399 fn line(&self, line_idx: usize) -> Option<Line<'_>> {
400 let line = self.rope.get_line(line_idx);
401
402 line.map(|line| Line {
403 text: Cow::Owned(line.to_string()),
404 utf16_len: line.len_utf16_cu(),
405 })
406 }
407
408 fn len_lines(&self) -> usize {
409 self.rope.len_lines()
410 }
411
412 fn len_chars(&self) -> usize {
413 self.rope.len_chars()
414 }
415
416 fn len_utf16_cu(&self) -> usize {
417 self.rope.len_utf16_cu()
418 }
419
420 fn has_any_selection(&self) -> bool {
421 self.selection.is_range()
422 }
423
424 fn get_selection(&self) -> Option<(usize, usize)> {
425 match self.selection {
426 TextSelection::Cursor(_) => None,
427 TextSelection::Range { from, to } => Some((from, to)),
428 }
429 }
430
431 fn set(&mut self, text: &str) {
432 self.rope.remove(0..);
433 self.rope.insert(0, text);
434 }
435
436 fn clear_selection(&mut self) {
437 let end = self.selection().end();
438 self.selection_mut().set_as_cursor();
439 self.selection_mut().move_to(end);
440 }
441
442 fn set_selection(&mut self, (from, to): (usize, usize)) {
443 self.selection = TextSelection::Range { from, to };
444 }
445
446 fn get_selected_text(&self) -> Option<String> {
447 let (start, end) = self.get_selection_range()?;
448
449 Some(self.rope.get_slice(start..end)?.to_string())
450 }
451
452 fn get_selection_range(&self) -> Option<(usize, usize)> {
453 let (start, end) = match self.selection {
454 TextSelection::Cursor(_) => return None,
455 TextSelection::Range { from, to } => (from, to),
456 };
457
458 let (start, end) = if start < end {
460 (start, end)
461 } else {
462 (end, start)
463 };
464
465 Some((start, end))
466 }
467
468 fn undo(&mut self) -> Option<TextSelection> {
469 self.pending_edit = None;
471 self.metrics.highlighter.invalidate_tree();
472 self.history.undo(&mut self.rope)
473 }
474
475 fn redo(&mut self) -> Option<TextSelection> {
476 self.pending_edit = None;
478 self.metrics.highlighter.invalidate_tree();
479 self.history.redo(&mut self.rope)
480 }
481
482 fn editor_history(&self) -> &EditorHistory {
483 &self.history
484 }
485
486 fn editor_history_mut(&mut self) -> &mut EditorHistory {
487 &mut self.history
488 }
489
490 fn selection(&self) -> &TextSelection {
491 &self.selection
492 }
493
494 fn selection_mut(&mut self) -> &mut TextSelection {
495 &mut self.selection
496 }
497
498 fn get_indentation(&self) -> u8 {
499 4
500 }
501}