diff options
Diffstat (limited to 'stmhal/pyexec.c')
-rw-r--r-- | stmhal/pyexec.c | 122 |
1 files changed, 100 insertions, 22 deletions
diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c index ecdd4d8c6d..f1aa7dcad4 100644 --- a/stmhal/pyexec.c +++ b/stmhal/pyexec.c @@ -38,6 +38,16 @@ void stdout_tx_str(const char *str) { usb_vcp_send_str(str); } +void stdout_tx_strn(const char *str, uint len) { + if (pyb_usart_global_debug != PYB_USART_NONE) { + usart_tx_strn(pyb_usart_global_debug, str, len); + } +#if defined(USE_HOST_MODE) && MICROPY_HW_HAS_LCD + lcd_print_strn(str, len); +#endif + usb_vcp_send_strn(str, len); +} + int stdin_rx_chr(void) { for (;;) { #if 0 @@ -77,36 +87,49 @@ static const char *readline_hist[READLINE_HIST_SIZE] = {NULL, NULL, NULL, NULL, int readline(vstr_t *line, const char *prompt) { stdout_tx_str(prompt); - int len = vstr_len(line); + int orig_line_len = line->len; int escape_seq = 0; - int hist_num = 0; + int hist_cur = -1; + int cursor_pos = orig_line_len; for (;;) { int c = stdin_rx_chr(); + int last_line_len = line->len; + int redraw_step_back = 0; + bool redraw_from_cursor = false; + int redraw_step_forward = 0; if (escape_seq == 0) { - if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len(line) == len) { + if (VCP_CHAR_CTRL_A <= c && c <= VCP_CHAR_CTRL_D && vstr_len(line) == orig_line_len) { // control character with empty line return c; } else if (c == '\r') { // newline stdout_tx_str("\r\n"); - for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { - readline_hist[i] = readline_hist[i - 1]; + if (line->len > orig_line_len && (readline_hist[0] == NULL || strcmp(readline_hist[0], line->buf + orig_line_len) != 0)) { + // a line which is not empty and different from the last one + // so update the history + for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { + readline_hist[i] = readline_hist[i - 1]; + } + readline_hist[0] = str_dup(line->buf + orig_line_len); } - readline_hist[0] = str_dup(vstr_str(line)); return 0; } else if (c == 27) { // escape sequence escape_seq = 1; } else if (c == 127) { // backspace - if (vstr_len(line) > len) { - vstr_cut_tail(line, 1); - stdout_tx_str("\b \b"); + if (cursor_pos > orig_line_len) { + vstr_cut_out_bytes(line, cursor_pos - 1, 1); + // set redraw parameters + redraw_step_back = 1; + redraw_from_cursor = true; } } else if (32 <= c && c <= 126) { // printable character - vstr_add_char(line, c); - stdout_tx_str(line->buf + line->len - 1); + vstr_ins_char(line, cursor_pos, c); + // set redraw parameters + redraw_from_cursor = true; + redraw_step_forward = 1; } } else if (escape_seq == 1) { if (c == '[') { @@ -118,23 +141,78 @@ int readline(vstr_t *line, const char *prompt) { escape_seq = 0; if (c == 'A') { // up arrow - if (hist_num < READLINE_HIST_SIZE && readline_hist[hist_num] != NULL) { - // erase line - for (int i = line->len - len; i > 0; i--) { - stdout_tx_str("\b \b"); - } - // set line to history - line->len = len; - vstr_add_str(line, readline_hist[hist_num]); - // draw line - stdout_tx_str(readline_hist[hist_num]); + if (hist_cur + 1 < READLINE_HIST_SIZE && readline_hist[hist_cur + 1] != NULL) { // increase hist num - hist_num += 1; + hist_cur += 1; + // set line to history + line->len = orig_line_len; + vstr_add_str(line, readline_hist[hist_cur]); + // set redraw parameters + redraw_step_back = cursor_pos - orig_line_len; + redraw_from_cursor = true; + redraw_step_forward = line->len - orig_line_len; + } + } else if (c == 'B') { + // down arrow + if (hist_cur >= 0) { + // decrease hist num + hist_cur -= 1; + // set line to history + vstr_cut_tail_bytes(line, line->len - orig_line_len); + if (hist_cur >= 0) { + vstr_add_str(line, readline_hist[hist_cur]); + } + // set redraw parameters + redraw_step_back = cursor_pos - orig_line_len; + redraw_from_cursor = true; + redraw_step_forward = line->len - orig_line_len; + } + } else if (c == 'C') { + // right arrow + if (cursor_pos < line->len) { + redraw_step_forward = 1; + } + } else if (c == 'D') { + // left arrow + if (cursor_pos > orig_line_len) { + redraw_step_back = 1; } } } else { escape_seq = 0; } + + // redraw command prompt, efficiently + if (redraw_step_back > 0) { + for (int i = 0; i < redraw_step_back; i++) { + stdout_tx_str("\b"); + } + cursor_pos -= redraw_step_back; + } + if (redraw_from_cursor) { + if (line->len < last_line_len) { + // erase old chars + for (int i = cursor_pos; i < last_line_len; i++) { + stdout_tx_str(" "); + } + // step back + for (int i = cursor_pos; i < last_line_len; i++) { + stdout_tx_str("\b"); + } + } + // draw new chars + stdout_tx_strn(line->buf + cursor_pos, line->len - cursor_pos); + // move cursor forward if needed (already moved forward by length of line, so move it back) + for (int i = cursor_pos + redraw_step_forward; i < line->len; i++) { + stdout_tx_str("\b"); + } + cursor_pos += redraw_step_forward; + } else if (redraw_step_forward > 0) { + // draw over old chars to move cursor forwards + stdout_tx_strn(line->buf + cursor_pos, redraw_step_forward); + cursor_pos += redraw_step_forward; + } + HAL_Delay(1); } } |