@@ -38,6 +38,16 @@ void stdout_tx_str(const char *str) {
3838 usb_vcp_send_str (str );
3939}
4040
41+ void stdout_tx_strn (const char * str , uint len ) {
42+ if (pyb_usart_global_debug != PYB_USART_NONE ) {
43+ usart_tx_strn (pyb_usart_global_debug , str , len );
44+ }
45+ #if defined(USE_HOST_MODE ) && MICROPY_HW_HAS_LCD
46+ lcd_print_strn (str , len );
47+ #endif
48+ usb_vcp_send_strn (str , len );
49+ }
50+
4151int stdin_rx_chr (void ) {
4252 for (;;) {
4353#if 0
@@ -77,36 +87,49 @@ static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL,
7787
7888int readline (vstr_t * line , const char * prompt ) {
7989 stdout_tx_str (prompt );
80- int len = vstr_len ( line ) ;
90+ int orig_line_len = line -> len ;
8191 int escape_seq = 0 ;
82- int hist_num = 0 ;
92+ int hist_cur = -1 ;
93+ int cursor_pos = orig_line_len ;
8394 for (;;) {
8495 int c = stdin_rx_chr ();
96+ int last_line_len = line -> len ;
97+ int redraw_step_back = 0 ;
98+ bool redraw_from_cursor = false;
99+ int redraw_step_forward = 0 ;
85100 if (escape_seq == 0 ) {
86- if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len (line ) == len ) {
101+ if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len (line ) == orig_line_len ) {
87102 // control character with empty line
88103 return c ;
89104 } else if (c == '\r' ) {
90105 // newline
91106 stdout_tx_str ("\r\n" );
92- for (int i = READLINE_HIST_SIZE - 1 ; i > 0 ; i -- ) {
93- readline_hist [i ] = readline_hist [i - 1 ];
107+ if (line -> len > orig_line_len && (readline_hist [0 ] == NULL || strcmp (readline_hist [0 ], line -> buf + orig_line_len ) != 0 )) {
108+ // a line which is not empty and different from the last one
109+ // so update the history
110+ for (int i = READLINE_HIST_SIZE - 1 ; i > 0 ; i -- ) {
111+ readline_hist [i ] = readline_hist [i - 1 ];
112+ }
113+ readline_hist [0 ] = str_dup (line -> buf + orig_line_len );
94114 }
95- readline_hist [0 ] = str_dup (vstr_str (line ));
96115 return 0 ;
97116 } else if (c == 27 ) {
98117 // escape sequence
99118 escape_seq = 1 ;
100119 } else if (c == 127 ) {
101120 // backspace
102- if (vstr_len (line ) > len ) {
103- vstr_cut_tail (line , 1 );
104- stdout_tx_str ("\b \b" );
121+ if (cursor_pos > orig_line_len ) {
122+ vstr_cut_out_bytes (line , cursor_pos - 1 , 1 );
123+ // set redraw parameters
124+ redraw_step_back = 1 ;
125+ redraw_from_cursor = true;
105126 }
106127 } else if (32 <= c && c <= 126 ) {
107128 // printable character
108- vstr_add_char (line , c );
109- stdout_tx_str (line -> buf + line -> len - 1 );
129+ vstr_ins_char (line , cursor_pos , c );
130+ // set redraw parameters
131+ redraw_from_cursor = true;
132+ redraw_step_forward = 1 ;
110133 }
111134 } else if (escape_seq == 1 ) {
112135 if (c == '[' ) {
@@ -118,23 +141,78 @@ int readline(vstr_t *line, const char *prompt) {
118141 escape_seq = 0 ;
119142 if (c == 'A' ) {
120143 // up arrow
121- if (hist_num < READLINE_HIST_SIZE && readline_hist [hist_num ] != NULL ) {
122- // erase line
123- for (int i = line -> len - len ; i > 0 ; i -- ) {
124- stdout_tx_str ("\b \b" );
125- }
126- // set line to history
127- line -> len = len ;
128- vstr_add_str (line , readline_hist [hist_num ]);
129- // draw line
130- stdout_tx_str (readline_hist [hist_num ]);
144+ if (hist_cur + 1 < READLINE_HIST_SIZE && readline_hist [hist_cur + 1 ] != NULL ) {
131145 // increase hist num
132- hist_num += 1 ;
146+ hist_cur += 1 ;
147+ // set line to history
148+ line -> len = orig_line_len ;
149+ vstr_add_str (line , readline_hist [hist_cur ]);
150+ // set redraw parameters
151+ redraw_step_back = cursor_pos - orig_line_len ;
152+ redraw_from_cursor = true;
153+ redraw_step_forward = line -> len - orig_line_len ;
154+ }
155+ } else if (c == 'B' ) {
156+ // down arrow
157+ if (hist_cur >= 0 ) {
158+ // decrease hist num
159+ hist_cur -= 1 ;
160+ // set line to history
161+ vstr_cut_tail_bytes (line , line -> len - orig_line_len );
162+ if (hist_cur >= 0 ) {
163+ vstr_add_str (line , readline_hist [hist_cur ]);
164+ }
165+ // set redraw parameters
166+ redraw_step_back = cursor_pos - orig_line_len ;
167+ redraw_from_cursor = true;
168+ redraw_step_forward = line -> len - orig_line_len ;
169+ }
170+ } else if (c == 'C' ) {
171+ // right arrow
172+ if (cursor_pos < line -> len ) {
173+ redraw_step_forward = 1 ;
174+ }
175+ } else if (c == 'D' ) {
176+ // left arrow
177+ if (cursor_pos > orig_line_len ) {
178+ redraw_step_back = 1 ;
133179 }
134180 }
135181 } else {
136182 escape_seq = 0 ;
137183 }
184+
185+ // redraw command prompt, efficiently
186+ if (redraw_step_back > 0 ) {
187+ for (int i = 0 ; i < redraw_step_back ; i ++ ) {
188+ stdout_tx_str ("\b" );
189+ }
190+ cursor_pos -= redraw_step_back ;
191+ }
192+ if (redraw_from_cursor ) {
193+ if (line -> len < last_line_len ) {
194+ // erase old chars
195+ for (int i = cursor_pos ; i < last_line_len ; i ++ ) {
196+ stdout_tx_str (" " );
197+ }
198+ // step back
199+ for (int i = cursor_pos ; i < last_line_len ; i ++ ) {
200+ stdout_tx_str ("\b" );
201+ }
202+ }
203+ // draw new chars
204+ stdout_tx_strn (line -> buf + cursor_pos , line -> len - cursor_pos );
205+ // move cursor forward if needed (already moved forward by length of line, so move it back)
206+ for (int i = cursor_pos + redraw_step_forward ; i < line -> len ; i ++ ) {
207+ stdout_tx_str ("\b" );
208+ }
209+ cursor_pos += redraw_step_forward ;
210+ } else if (redraw_step_forward > 0 ) {
211+ // draw over old chars to move cursor forwards
212+ stdout_tx_strn (line -> buf + cursor_pos , redraw_step_forward );
213+ cursor_pos += redraw_step_forward ;
214+ }
215+
138216 HAL_Delay (1 );
139217 }
140218}
0 commit comments