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 { key, modifiers } => {
193 match key {
194 Key::Named(NamedKey::Shift) => {
196 self.dragging.shift = true;
197 }
198 _ => {
200 let event = self.process_key(key, &modifiers, true, true, true, true);
201 if event.contains(TextEvent::TEXT_CHANGED) {
202 self.parse();
203 self.measure(font_size, font_family);
204 self.dragging = TextDragging::default();
205 }
206 if !event.is_empty() {
207 processed = true;
208 }
209 }
210 }
211 }
212 EditableEvent::KeyUp { key, .. } => {
213 if *key == Key::Named(NamedKey::Shift) {
214 self.dragging.shift = false;
215 }
216 }
217 };
218 processed
219 }
220}
221
222impl Display for CodeEditorData {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 f.write_str(&self.rope.to_string())
225 }
226}
227
228impl TextEditor for CodeEditorData {
229 type LinesIterator<'a>
230 = LinesIterator<'a>
231 where
232 Self: 'a;
233
234 fn lines(&self) -> Self::LinesIterator<'_> {
235 unimplemented!("Unused.")
236 }
237
238 fn insert_char(&mut self, ch: char, idx: usize) -> usize {
239 let idx_utf8 = self.utf16_cu_to_char(idx);
240 let selection = self.selection.clone();
241
242 let start_byte = self.rope.char_to_byte(idx_utf8);
244 let start_line = self.rope.char_to_line(idx_utf8);
245 let start_line_byte = self.rope.line_to_byte(start_line);
246 let start_col = start_byte - start_line_byte;
247
248 let len_before_insert = self.rope.len_utf16_cu();
249 self.rope.insert_char(idx_utf8, ch);
250 let len_after_insert = self.rope.len_utf16_cu();
251
252 let inserted_text_len = len_after_insert - len_before_insert;
253
254 let new_end_char = idx_utf8 + 1; let new_end_byte = self.rope.char_to_byte(new_end_char);
257 let new_end_line = self.rope.char_to_line(new_end_char);
258 let new_end_line_byte = self.rope.line_to_byte(new_end_line);
259 let new_end_col = new_end_byte - new_end_line_byte;
260
261 self.pending_edit = Some(InputEdit::new_edit(
262 start_byte,
263 start_byte,
264 new_end_byte,
265 (start_line, start_col),
266 (start_line, start_col),
267 (new_end_line, new_end_col),
268 ));
269
270 self.history.push_change(HistoryChange::InsertChar {
271 idx,
272 ch,
273 len: inserted_text_len,
274 selection,
275 });
276
277 inserted_text_len
278 }
279
280 fn insert(&mut self, text: &str, idx: usize) -> usize {
281 let idx_utf8 = self.utf16_cu_to_char(idx);
282 let selection = self.selection.clone();
283
284 let start_byte = self.rope.char_to_byte(idx_utf8);
286 let start_line = self.rope.char_to_line(idx_utf8);
287 let start_line_byte = self.rope.line_to_byte(start_line);
288 let start_col = start_byte - start_line_byte;
289
290 let len_before_insert = self.rope.len_utf16_cu();
291 self.rope.insert(idx_utf8, text);
292 let len_after_insert = self.rope.len_utf16_cu();
293
294 let inserted_text_len = len_after_insert - len_before_insert;
295
296 let inserted_chars = text.chars().count();
298 let new_end_char = idx_utf8 + inserted_chars;
299 let new_end_byte = self.rope.char_to_byte(new_end_char);
300 let new_end_line = self.rope.char_to_line(new_end_char);
301 let new_end_line_byte = self.rope.line_to_byte(new_end_line);
302 let new_end_col = new_end_byte - new_end_line_byte;
303
304 self.pending_edit = Some(InputEdit::new_edit(
305 start_byte,
306 start_byte,
307 new_end_byte,
308 (start_line, start_col),
309 (start_line, start_col),
310 (new_end_line, new_end_col),
311 ));
312
313 self.history.push_change(HistoryChange::InsertText {
314 idx,
315 text: text.to_owned(),
316 len: inserted_text_len,
317 selection,
318 });
319
320 inserted_text_len
321 }
322
323 fn remove(&mut self, range_utf16: Range<usize>) -> usize {
324 let range =
325 self.utf16_cu_to_char(range_utf16.start)..self.utf16_cu_to_char(range_utf16.end);
326 let text = self.rope.slice(range.clone()).to_string();
327 let selection = self.selection.clone();
328
329 let start_byte = self.rope.char_to_byte(range.start);
331 let old_end_byte = self.rope.char_to_byte(range.end);
332 let start_line = self.rope.char_to_line(range.start);
333 let start_line_byte = self.rope.line_to_byte(start_line);
334 let start_col = start_byte - start_line_byte;
335 let old_end_line = self.rope.char_to_line(range.end);
336 let old_end_line_byte = self.rope.line_to_byte(old_end_line);
337 let old_end_col = old_end_byte - old_end_line_byte;
338
339 let len_before_remove = self.rope.len_utf16_cu();
340 self.rope.remove(range);
341 let len_after_remove = self.rope.len_utf16_cu();
342
343 let removed_text_len = len_before_remove - len_after_remove;
344
345 self.pending_edit = Some(InputEdit::new_edit(
347 start_byte,
348 old_end_byte,
349 start_byte,
350 (start_line, start_col),
351 (old_end_line, old_end_col),
352 (start_line, start_col),
353 ));
354
355 self.history.push_change(HistoryChange::Remove {
356 idx: range_utf16.end - removed_text_len,
357 text,
358 len: removed_text_len,
359 selection,
360 });
361
362 removed_text_len
363 }
364
365 fn char_to_line(&self, char_idx: usize) -> usize {
366 self.rope.char_to_line(char_idx)
367 }
368
369 fn line_to_char(&self, line_idx: usize) -> usize {
370 self.rope.line_to_char(line_idx)
371 }
372
373 fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
374 self.rope.utf16_cu_to_char(utf16_cu_idx)
375 }
376
377 fn char_to_utf16_cu(&self, idx: usize) -> usize {
378 self.rope.char_to_utf16_cu(idx)
379 }
380
381 fn line(&self, line_idx: usize) -> Option<Line<'_>> {
382 let line = self.rope.get_line(line_idx);
383
384 line.map(|line| Line {
385 text: Cow::Owned(line.to_string()),
386 utf16_len: line.len_utf16_cu(),
387 })
388 }
389
390 fn len_lines(&self) -> usize {
391 self.rope.len_lines()
392 }
393
394 fn len_chars(&self) -> usize {
395 self.rope.len_chars()
396 }
397
398 fn len_utf16_cu(&self) -> usize {
399 self.rope.len_utf16_cu()
400 }
401
402 fn has_any_selection(&self) -> bool {
403 self.selection.is_range()
404 }
405
406 fn get_selection(&self) -> Option<(usize, usize)> {
407 match self.selection {
408 TextSelection::Cursor(_) => None,
409 TextSelection::Range { from, to } => Some((from, to)),
410 }
411 }
412
413 fn set(&mut self, text: &str) {
414 self.rope.remove(0..);
415 self.rope.insert(0, text);
416 }
417
418 fn clear_selection(&mut self) {
419 let end = self.selection().end();
420 self.selection_mut().set_as_cursor();
421 self.selection_mut().move_to(end);
422 }
423
424 fn set_selection(&mut self, (from, to): (usize, usize)) {
425 self.selection = TextSelection::Range { from, to };
426 }
427
428 fn get_selected_text(&self) -> Option<String> {
429 let (start, end) = self.get_selection_range()?;
430
431 Some(self.rope.get_slice(start..end)?.to_string())
432 }
433
434 fn get_selection_range(&self) -> Option<(usize, usize)> {
435 let (start, end) = match self.selection {
436 TextSelection::Cursor(_) => return None,
437 TextSelection::Range { from, to } => (from, to),
438 };
439
440 let (start, end) = if start < end {
442 (start, end)
443 } else {
444 (end, start)
445 };
446
447 Some((start, end))
448 }
449
450 fn undo(&mut self) -> Option<TextSelection> {
451 self.pending_edit = None;
453 self.metrics.highlighter.invalidate_tree();
454 self.history.undo(&mut self.rope)
455 }
456
457 fn redo(&mut self) -> Option<TextSelection> {
458 self.pending_edit = None;
460 self.metrics.highlighter.invalidate_tree();
461 self.history.redo(&mut self.rope)
462 }
463
464 fn editor_history(&mut self) -> &mut EditorHistory {
465 &mut self.history
466 }
467
468 fn selection(&self) -> &TextSelection {
469 &self.selection
470 }
471
472 fn selection_mut(&mut self) -> &mut TextSelection {
473 &mut self.selection
474 }
475
476 fn get_indentation(&self) -> u8 {
477 4
478 }
479}