11extern crate crossterm;
22extern crate ratatui;
3- use std:: io ;
3+ use std:: { io , iter :: FromIterator } ;
44
55use crossterm:: event:: { self , Event , KeyCode , KeyEvent , KeyModifiers } ;
6- use ratatui:: { DefaultTerminal , Frame } ;
6+ use ratatui:: {
7+ layout:: Position ,
8+ text:: { Line , Span , Text } ,
9+ DefaultTerminal , Frame ,
10+ } ;
711
812struct State {
9- text : Vec < char > ,
13+ lines : Vec < Vec < char > > ,
14+ cursor : Position ,
1015}
1116
1217impl State {
@@ -26,29 +31,104 @@ impl State {
2631 modifiers : KeyModifiers :: NONE ,
2732 ..
2833 } ) => self . insert_char ( c) ,
34+ Event :: Key ( KeyEvent {
35+ code : KeyCode :: Backspace ,
36+ modifiers : KeyModifiers :: NONE ,
37+ ..
38+ } ) => self . delete_prev_char ( ) ,
2939 Event :: Key ( KeyEvent {
3040 code : KeyCode :: Enter ,
3141 modifiers : KeyModifiers :: NONE ,
3242 ..
33- } ) => self . insert_char ( '\n' ) ,
43+ } ) => self . insert_line ( ) ,
44+ Event :: Key ( KeyEvent {
45+ code : KeyCode :: Left ,
46+ modifiers : KeyModifiers :: NONE ,
47+ ..
48+ } ) => self . move_left ( ) ,
49+ Event :: Key ( KeyEvent {
50+ code : KeyCode :: Right ,
51+ modifiers : KeyModifiers :: NONE ,
52+ ..
53+ } ) => self . move_right ( ) ,
54+
3455 _ => { }
3556 }
3657 }
3758 }
3859
3960 fn insert_char ( & mut self , c : char ) {
40- self . text . push ( c) ;
61+ let line = self . lines . get_mut ( self . cursor . y as usize ) ;
62+ match line {
63+ None => { }
64+ Some ( v) => {
65+ v. insert ( self . cursor . x as usize , c) ;
66+ self . cursor . x += 1 ;
67+ }
68+ }
69+ }
70+
71+ fn delete_prev_char ( & mut self ) {
72+ if self . cursor . x > 0 {
73+ self . cursor . x -= 1 ;
74+ let line = self . lines . get_mut ( self . cursor . y as usize ) . unwrap ( ) ;
75+ line. remove ( self . cursor . x as usize ) ;
76+ } else if self . cursor . y > 0 {
77+ self . cursor . y -= 1 ;
78+ let line = self . lines . remove ( ( self . cursor . y + 1 ) as usize ) ;
79+ let prev_line = self . lines . get_mut ( self . cursor . y as usize ) . unwrap ( ) ;
80+ self . cursor . x = prev_line. len ( ) as u16 ;
81+ prev_line. extend ( line) ;
82+ } else {
83+ return ;
84+ }
85+ }
86+
87+ fn insert_line ( & mut self ) {
88+ self . cursor . y += 1 ;
89+ self . cursor . x = 0 ;
90+ self . lines . insert ( self . cursor . y as usize , vec ! [ ] ) ;
4191 }
4292
4393 fn render ( & self , frame : & mut Frame ) {
44- let s: String = self . text . iter ( ) . collect ( ) ;
45- frame. render_widget ( s, frame. area ( ) ) ;
94+ fn to_line ( l : & Vec < char > ) -> Line < ' _ > {
95+ let content = l. iter ( ) . collect :: < String > ( ) ;
96+ Line :: from ( Span :: raw ( content) )
97+ }
98+ frame. render_widget (
99+ Text :: from_iter ( self . lines . iter ( ) . map ( to_line) ) ,
100+ frame. area ( ) ,
101+ ) ;
102+ frame. set_cursor_position ( self . cursor ) ;
103+ }
104+
105+ fn move_left ( & mut self ) {
106+ if self . cursor . x > 0 {
107+ self . cursor . x -= 1 ;
108+ } else if self . cursor . y > 0 {
109+ self . cursor . y -= 1 ;
110+ let prev_line = self . lines . get ( self . cursor . y as usize ) . unwrap ( ) ;
111+ self . cursor . x = prev_line. len ( ) as u16 ;
112+ }
113+ }
114+
115+ fn move_right ( & mut self ) {
116+ let line = self . lines . get ( self . cursor . y as usize ) . unwrap ( ) ;
117+ if self . cursor . x < line. len ( ) as u16 {
118+ self . cursor . x += 1 ;
119+ } else if self . cursor . y + 1 < self . lines . len ( ) as u16 {
120+ self . cursor . y += 1 ;
121+ self . cursor . x = 0 ;
122+ }
46123 }
47124}
48125
49126fn main ( ) -> io:: Result < ( ) > {
50127 let terminal = ratatui:: init ( ) ;
51- let mut state: State = State { text : Vec :: new ( ) } ;
128+ let mut state: State = State {
129+ lines : vec ! [ vec![ ] ] ,
130+ cursor : Position :: new ( 0 , 0 ) ,
131+ } ;
52132 let result = state. run ( terminal) ;
53133 ratatui:: restore ( ) ;
54134 result
0 commit comments