1use log::debug;
19use std::cmp;
20use std::fs::File;
21use std::io::{self, BufRead, BufReader};
22
23use unicode_width::UnicodeWidthStr;
24
25use crate::app::core::AppReturn;
26use crate::app::datafusion::context::QueryResultsMeta;
27use crate::app::error::Result;
28
29const MAX_EDITOR_LINES: u16 = 17;
30
31#[derive(Debug)]
33pub struct Line {
34 text: io::Cursor<String>,
35}
36
37impl Default for Line {
38 fn default() -> Line {
39 Line {
40 text: io::Cursor::new(String::new()),
41 }
42 }
43}
44
45impl Line {
46 pub fn new(text: String) -> Self {
47 Line {
48 text: io::Cursor::new(text),
49 }
50 }
51}
52
53#[derive(Debug, Default)]
55pub struct Input {
56 pub lines: Vec<Line>,
57 pub current_row: u16,
59 pub cursor_column: u16,
61}
62
63impl Input {
64 pub fn combine_lines(&self) -> String {
65 let text: Vec<&str> = self
66 .lines
67 .iter()
68 .map(|line| line.text.get_ref().as_str())
70 .collect();
71 text.join("")
72 }
73
74 pub fn combine_visible_lines(&self) -> String {
75 let start = if self.current_row < MAX_EDITOR_LINES {
76 0
77 } else {
78 self.current_row - MAX_EDITOR_LINES
79 } as usize;
80
81 let end = (start + (MAX_EDITOR_LINES as usize) + 1) as usize;
82
83 let text: Vec<&str> = if start == 0 {
84 self.lines
86 .iter()
87 .map(|line| line.text.get_ref().as_str())
88 .collect()
89 } else {
90 debug!("Combining visible lines: start({}) to end({})", start, end);
91 self.lines[start..end]
92 .iter()
93 .map(|line| line.text.get_ref().as_str())
94 .collect()
95 };
96
97 text.join("")
98 }
99
100 pub fn append_char(&mut self, c: char) -> Result<AppReturn> {
101 if self.lines.is_empty() {
102 let line = Line::default();
103 self.lines.push(line)
104 }
105 match c {
106 '\n' => self.new_line(),
107 '\t' => {
108 self.lines[self.current_row as usize]
109 .text
110 .get_mut()
111 .push_str(" ");
112 self.cursor_column += 4
113 }
114 _ => {
115 self.lines[self.current_row as usize]
116 .text
117 .get_mut()
118 .insert(self.cursor_column as usize, c);
119 debug!(
120 "Line after appending {:?} : {:?}",
121 c,
122 self.lines[self.current_row as usize].text.get_ref()
123 );
124 self.cursor_column += 1;
125 }
126 }
127 Ok(AppReturn::Continue)
128 }
129
130 pub fn pop(&mut self) -> Option<char> {
132 self.lines[self.current_row as usize].text.get_mut().pop()
133 }
134
135 pub fn up_row(&mut self) -> Result<AppReturn> {
137 if self.current_row > 0 {
138 match self.lines[self.current_row as usize]
139 .text
140 .get_ref()
141 .is_empty()
142 {
143 true => {
144 self.current_row -= 1;
145 let new_row_width =
146 self.lines[self.current_row as usize].text.get_ref().width() as u16;
147 self.cursor_column = new_row_width;
148 }
149 false => {
150 let previous_col = self.cursor_column;
151 self.current_row -= 1;
152 let new_row_width =
153 self.lines[self.current_row as usize].text.get_ref().width() as u16;
154 let new_col = cmp::min(previous_col, new_row_width);
155 self.cursor_column = new_col;
156 }
157 }
158 }
159 Ok(AppReturn::Continue)
160 }
161
162 pub fn down_row(&mut self) -> Result<AppReturn> {
164 if self.lines.is_empty() {
165 return Ok(AppReturn::Continue);
166 } else if self.current_row + 1 < self.lines.len() as u16 {
167 let previous_col = self.cursor_column;
168 self.current_row += 1;
169 let new_row_width = self.lines[self.current_row as usize].text.get_ref().width() as u16;
170 let new_col = cmp::min(previous_col, new_row_width);
171 self.cursor_column = new_col;
172 }
173 Ok(AppReturn::Continue)
174 }
175
176 pub fn next_char(&mut self) -> Result<AppReturn> {
178 if self.lines.is_empty()
179 || self.cursor_column
180 == self.lines[self.current_row as usize].text.get_ref().width() as u16
181 {
182 return Ok(AppReturn::Continue);
183 } else if (self.cursor_column + 1
184 == self.lines[self.current_row as usize].text.get_ref().width() as u16)
185 && (self.current_row as usize != self.lines.len() - 1)
186 {
187 self.current_row += 1;
188 self.cursor_column = 0
189 } else {
190 self.cursor_column += 1
191 }
192 Ok(AppReturn::Continue)
193 }
194
195 pub fn previous_char(&mut self) -> Result<AppReturn> {
197 if (self.cursor_column == 0) && (self.current_row > 0) {
198 self.current_row -= 1;
199 self.cursor_column = self.lines[self.current_row as usize].text.get_ref().width() as u16
200 } else if self.cursor_column > 0 {
201 self.cursor_column -= 1
202 }
203 Ok(AppReturn::Continue)
204 }
205
206 #[allow(dead_code)]
207 fn number_chars_in_current_line(&self) -> usize {
214 self.lines[self.current_row as usize]
215 .text
216 .get_ref()
217 .chars()
218 .count()
219 }
220
221 pub fn backspace(&mut self) -> Result<AppReturn> {
222 debug!("Backspace entered. Input Before: {:?}", self);
223 match self.lines[self.current_row as usize]
224 .text
225 .get_ref()
226 .is_empty()
227 {
228 true => {
229 self.up_row()?;
230 self.pop();
232 }
233 false => {
234 if self.cursor_is_at_line_beginning() {
235 if self.on_first_line() {
236 debug!("On first column of first line. Unable to backspace")
237 } else {
238 let prior_row_text =
239 self.lines[self.current_row as usize].text.get_ref().clone();
240 self.up_row()?;
241 self.pop();
242 self.lines[self.current_row as usize]
243 .text
244 .get_mut()
245 .push_str(&prior_row_text);
246 self.lines.remove((self.current_row + 1) as usize);
247 }
248 } else if self.cursor_is_at_line_end() || self.cursor_is_in_line_middle() {
249 self.lines[self.current_row as usize]
250 .text
251 .get_mut()
252 .remove((self.cursor_column - 1) as usize);
253 self.cursor_column -= 1
254 }
255 }
256 };
257 debug!("Input After: {:?}", self);
258 Ok(AppReturn::Continue)
259 }
260
261 pub fn clear(&mut self) -> Result<AppReturn> {
262 let lines = Vec::<Line>::new();
263 self.lines = lines;
264 self.current_row = 0;
265 self.cursor_column = 0;
266 Ok(AppReturn::Continue)
267 }
268
269 pub fn tab(&mut self) -> Result<AppReturn> {
270 self.append_char('\t')
271 }
272
273 fn new_line(&mut self) {
274 if self.cursor_is_at_line_beginning() {
275 debug!("Cursor at line beginning");
276 let line_empty = self.lines[self.current_row as usize]
277 .text
278 .get_ref()
279 .is_empty();
280
281 let line = if line_empty {
282 Line::default()
283 } else {
284 let text = self.lines[self.current_row as usize].text.get_ref().clone();
285 Line::new(text)
286 };
287
288 self.lines[self.current_row as usize] = Line::new(String::from('\n'));
289 if self.on_last_line() {
290 self.lines.push(line);
291 } else {
292 self.lines.insert((self.current_row + 1) as usize, line)
293 }
294 self.current_row += 1;
295 self.cursor_column = 0;
296 debug!("Lines: {:?}", self.lines);
297 } else if self.cursor_is_at_line_end() {
298 debug!("Cursor at line end");
299 self.lines[self.current_row as usize]
300 .text
301 .get_mut()
302 .push('\n');
303 let line = Line::default();
304 if self.on_last_line() {
305 self.lines.push(line);
306 } else {
307 self.lines.insert((self.current_row + 1) as usize, line)
308 }
309 self.current_row += 1;
310 self.cursor_column = 0;
311 } else if self.cursor_is_in_line_middle() {
312 debug!("Cursor in middle of line");
313 let new_line: String = self.lines[self.current_row as usize]
314 .text
315 .get_mut()
316 .drain((self.cursor_column as usize)..)
317 .collect();
318 self.lines[self.current_row as usize]
319 .text
320 .get_mut()
321 .push('\n');
322
323 debug!("New line: {}", new_line);
324 let line = Line::new(new_line);
325 if self.on_last_line() {
326 self.lines.push(line);
327 } else {
328 self.lines.insert((self.current_row + 1) as usize, line)
329 }
330 self.current_row += 1;
331 self.cursor_column = 0;
332 } else {
333 debug!("Unhandled")
334 }
335 }
336
337 fn cursor_is_at_line_end(&self) -> bool {
338 let len = self.lines[self.current_row as usize].text.get_ref().len();
339 self.cursor_column as usize == len
340 }
341
342 fn cursor_is_at_line_beginning(&self) -> bool {
343 self.cursor_column == 0
344 }
345
346 fn cursor_is_in_line_middle(&self) -> bool {
347 let len = self.lines[self.current_row as usize].text.get_ref().len();
348 (self.cursor_column > 0) && ((self.cursor_column as usize) < len)
349 }
350
351 fn on_first_line(&self) -> bool {
352 let res = self.current_row as usize == 0;
353 debug!("On first line: {}", res);
354 res
355 }
356
357 fn on_last_line(&self) -> bool {
358 let res = self.current_row as usize == self.lines.len() - 1;
359 debug!("On last line: {}", res);
360 res
361 }
362}
363
364pub struct Editor {
366 pub input: Input,
368 pub sql_terminated: bool,
370 pub history: Vec<QueryResultsMeta>,
372}
373impl Default for Editor {
374 fn default() -> Editor {
375 let input = Input::default();
376 Editor {
377 input,
378 history: Vec::new(),
379 sql_terminated: false,
380 }
381 }
382}
383
384impl Editor {
385 pub fn get_cursor_row(&self) -> u16 {
386 if self.input.current_row < MAX_EDITOR_LINES {
387 self.input.current_row
388 } else {
389 MAX_EDITOR_LINES
390 }
391 }
392
393 pub fn get_cursor_column(&self) -> u16 {
394 self.input.cursor_column
395 }
396
397 pub fn load_file(&mut self, file: File) -> Result<()> {
398 let buf = BufReader::new(file);
399 let mut lines = Vec::new();
400 for line in buf.lines() {
401 let mut line = line?;
402 debug!("Line: {}", line);
403 line.push('\n');
404 let line = line.replace('\t', " ");
405 lines.push(Line::new(line));
406 }
407 self.input.lines = lines;
408 Ok(())
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use crate::app::editor::{Input, Line};
415 use std::io::Cursor;
416
417 #[test]
418 #[should_panic]
419 fn can_delete_non_ascii_characters() {
420 let mut input: Input = Input {
425 lines: vec![Line {
426 text: Cursor::new(String::from("äää")),
427 }],
428 current_row: 0,
429 cursor_column: 3,
430 };
431
432 input.backspace().expect("Expect that can delete character");
433 assert_eq!(input.current_row, 0);
434 assert_eq!(input.cursor_column, 2);
435
436 input.backspace().expect("Expect that can delete character");
437 assert_eq!(input.current_row, 0);
438 assert_eq!(input.cursor_column, 1);
439 }
440
441 #[test]
442 fn next_character_in_one_line() {
443 let mut input: Input = Input {
444 lines: vec![Line {
445 text: Cursor::new(String::from("aaa")),
446 }],
447 current_row: 0,
448 cursor_column: 0,
449 };
450
451 input.next_char().expect("Could move to next character");
452 assert_eq!(
453 input.cursor_column, 1,
454 "When moving once, cursor should be after first character"
455 );
456
457 input.next_char().expect("Could move to next character");
458 assert_eq!(input.cursor_column, 2);
459
460 input.next_char().expect("Could move to next character");
461 assert_eq!(input.cursor_column, 3);
462
463 input.next_char().expect("Could move to next character");
464 assert_eq!(
465 input.cursor_column, 3,
466 "When line is over and no next line exists, cursor should stop"
467 );
468 assert_eq!(
469 input.current_row, 0,
470 "When line is over and no next line exists, cursor should stop"
471 );
472 }
473
474 #[test]
475 fn previous_character_in_one_line() {
476 let mut input: Input = Input {
477 lines: vec![Line {
478 text: Cursor::new(String::from("aaa")),
479 }],
480 current_row: 0,
481 cursor_column: 3,
482 };
483
484 input
485 .previous_char()
486 .expect("Could move to previous character");
487 assert_eq!(input.cursor_column, 2);
488
489 input
490 .previous_char()
491 .expect("Could move to previous character");
492 assert_eq!(input.cursor_column, 1);
493
494 input
495 .previous_char()
496 .expect("Could move to previous character");
497 assert_eq!(input.cursor_column, 0);
498
499 input
500 .previous_char()
501 .expect("Could move to previous character");
502 assert_eq!(input.cursor_column, 0);
503 }
504
505 #[test]
506 #[ignore]
507 fn jump_to_next_line_on_next_character_at_the_end_of_line() {
508 let mut input: Input = Input {
510 lines: vec![
511 Line {
512 text: Cursor::new(String::from("aa")),
513 },
514 Line {
515 text: Cursor::new(String::from("bb")),
516 },
517 ],
518 current_row: 0,
519 cursor_column: 0,
520 };
521
522 input.next_char().expect("Could move to next character");
523 input.next_char().expect("Could move to next character");
524
525 input.next_char().expect("Could move to next character");
527 assert_eq!(
528 input.current_row, 1,
529 "Cursor should have jumped to next line"
530 );
531 assert_eq!(
532 input.cursor_column, 0,
533 "Cursor should be at beginning of the line"
534 );
535
536 input.next_char().expect("Could move to next character");
537 assert_eq!(input.current_row, 1);
538 assert_eq!(
539 input.cursor_column, 1,
540 "Cursor should be at the end of second line"
541 );
542
543 input.next_char().expect("Could move to next character");
544 assert_eq!(input.current_row, 1);
545 assert_eq!(input.cursor_column, 2);
546
547 input.next_char().expect("Could move to next character");
548 assert_eq!(input.current_row, 1);
549 assert_eq!(
550 input.cursor_column, 2,
551 "When there is no next line, cursor should stay unchanged"
552 );
553 }
554
555 #[test]
556 #[ignore]
557 fn jump_to_previous_line_on_previous_character_at_the_beginning_of_line() {
558 let mut input: Input = Input {
560 lines: vec![
561 Line {
562 text: Cursor::new(String::from("aa")),
563 },
564 Line {
565 text: Cursor::new(String::from("bb")),
566 },
567 ],
568 current_row: 1,
569 cursor_column: 0,
570 };
571
572 input.previous_char().expect("Could move to next character");
573 assert_eq!(
574 input.current_row, 0,
575 "Cursor should have jumped to previous line"
576 );
577 assert_eq!(
578 input.cursor_column, 1,
579 "Cursor should be at end of the previous line"
580 );
581 }
582
583 #[test]
584 fn non_ascii_character_count() {
585 let input: Input = Input {
586 lines: vec![Line {
587 text: Cursor::new(String::from("äää")),
588 }],
589 current_row: 0,
590 cursor_column: 0,
591 };
592
593 assert_eq!(input.number_chars_in_current_line(), 3);
594
595 let input2: Input = Input {
596 lines: vec![Line {
597 text: Cursor::new(String::from("äääb")),
598 }],
599 current_row: 0,
600 cursor_column: 0,
601 };
602 assert_eq!(input2.number_chars_in_current_line(), 4);
603 }
604
605 #[test]
606 #[should_panic]
607 fn test_append_char() {
608 let mut input: Input = Input::default();
613
614 input.append_char('ä').expect("Could append a character");
616 assert_eq!(input.current_row, 0);
617 assert_eq!(input.cursor_column, 1);
618 assert_eq!(input.number_chars_in_current_line(), 1);
619
620 input.append_char('b').expect("Could append a character");
622 assert_eq!(input.current_row, 0);
623 assert_eq!(input.cursor_column, 2);
624 assert_eq!(input.number_chars_in_current_line(), 2);
625
626 input.append_char('\t').expect("Could append a character");
628 assert_eq!(input.current_row, 0);
629 assert_eq!(input.cursor_column, 6);
630 assert_eq!(input.number_chars_in_current_line(), 6);
631
632 input.append_char('\n').expect("Could append a character");
634 assert_eq!(input.current_row, 1);
635 assert_eq!(input.cursor_column, 0);
636 assert_eq!(input.number_chars_in_current_line(), 0);
637
638 input.append_char('a').expect("Could append a character");
641 assert_eq!(input.current_row, 1);
642 assert_eq!(input.cursor_column, 1);
643 assert_eq!(input.number_chars_in_current_line(), 1);
644
645 input.up_row().expect("Can go up");
648 input.append_char('a').expect("Could append a character");
649 assert_eq!(input.current_row, 0);
650 assert_eq!(input.cursor_column, 2);
651 assert_eq!(
652 input.number_chars_in_current_line(),
653 7,
654 "Line: {}",
655 input.lines[input.current_row as usize].text.get_ref()
656 );
657
658 input.down_row().expect("Can go down");
661 input.previous_char().expect("Can go left");
662 input.append_char('b').expect("Can type a character");
663 assert_eq!(input.current_row, 1);
666 assert_eq!(input.cursor_column, 1);
667 assert_eq!(input.lines[1].text.get_ref(), "ba");
668 }
669
670 #[test]
671 fn test_up_row_and_down_row() {
672 let mut input: Input = Input {
673 lines: vec![
674 Line {
675 text: Cursor::new(String::from("aaaa")),
676 },
677 Line {
678 text: Cursor::new(String::from("bbbb")),
679 },
680 Line {
681 text: Cursor::new(String::from("cccc")),
682 },
683 Line {
684 text: Cursor::new(String::from("")),
685 },
686 Line {
687 text: Cursor::new(String::from("dddd")),
688 },
689 ],
690 current_row: 0,
691 cursor_column: 2,
692 };
693
694 input.up_row().expect("No exception should be thrown.");
695 assert_eq!(input.current_row, 0, "At 0th line, up_row has no effect");
696 assert_eq!(
697 input.cursor_column, 2,
698 "When up_row has no effect, the location inside the line should stay unchanged"
699 );
700
701 input.down_row().expect("No exception should be thrown.");
702 assert_eq!(input.current_row, 1);
703 assert_eq!(input.cursor_column, 2);
704
705 input.down_row().expect("No exception should be thrown.");
706 assert_eq!(input.current_row, 2);
707 assert_eq!(input.cursor_column, 2);
708
709 input.down_row().expect("No exception should be thrown.");
710 assert_eq!(input.current_row, 3);
711 assert_eq!(input.cursor_column, 0);
712
713 input.down_row().expect("No exception should be thrown.");
714 assert_eq!(input.current_row, 4);
715 assert_eq!(input.cursor_column, 0);
716
717 input.down_row().expect("No exception should be thrown.");
718 assert_eq!(input.current_row, 4, "At last line, down_row has no effect");
719 assert_eq!(input.cursor_column, 0);
720
721 input.up_row().expect("No exception should be thrown.");
722 assert_eq!(input.current_row, 3);
723 assert_eq!(
724 input.cursor_column, 0,
725 "When coming from an empty line, the cursor should be at 0th position."
726 );
727
728 let mut input2: Input = Input::default();
729 input2.append_char('a').expect("Can append char");
731 input2.append_char('\n').expect("Can append new line");
732 input2.up_row().expect("Can go up");
733 input2.down_row().expect("Can go down");
734 assert_eq!(input2.current_row, 1);
735 assert_eq!(input2.cursor_column, 0);
736 input2.append_char('b').expect("Can append char");
737 assert_eq!(input2.current_row, 1);
738 assert_eq!(input2.cursor_column, 1);
739 assert_eq!(input2.lines[0].text.get_ref(), "a\n");
740 assert_eq!(input2.lines[1].text.get_ref(), "b");
741 }
742}