Skip to content
Snippets Groups Projects
Commit 3f9f9cac authored by Paul Sokolovsky's avatar Paul Sokolovsky
Browse files

lib/mp-readline: Refactor to support coroutine/event-driven usage.

readline_process_char() can be fed character by character, for example,
received from external event loop. This will allow to integrate MicroPython
into cooperative multitasking systems.
parent 708574b0
No related branches found
No related tags found
No related merge requests found
...@@ -57,21 +57,24 @@ STATIC char *str_dup_maybe(const char *str) { ...@@ -57,21 +57,24 @@ STATIC char *str_dup_maybe(const char *str) {
return s2; return s2;
} }
int readline(vstr_t *line, const char *prompt) { typedef struct _readline_t {
stdout_tx_str(prompt); vstr_t *line;
int orig_line_len = line->len; int orig_line_len;
int escape_seq = ESEQ_NONE; int escape_seq;
char escape_seq_buf[1] = {0}; int hist_cur;
int hist_cur = -1; int cursor_pos;
int cursor_pos = orig_line_len; char escape_seq_buf[1];
for (;;) { } readline_t;
int c = stdin_rx_chr();
int last_line_len = line->len; readline_t rl;
int readline_process_char(int c) {
int last_line_len = rl.line->len;
int redraw_step_back = 0; int redraw_step_back = 0;
bool redraw_from_cursor = false; bool redraw_from_cursor = false;
int redraw_step_forward = 0; int redraw_step_forward = 0;
if (escape_seq == ESEQ_NONE) { if (rl.escape_seq == ESEQ_NONE) {
if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && vstr_len(line) == orig_line_len) { if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && vstr_len(rl.line) == rl.orig_line_len) {
// control character with empty line // control character with empty line
return c; return c;
} else if (c == CHAR_CTRL_A) { } else if (c == CHAR_CTRL_A) {
...@@ -86,10 +89,10 @@ int readline(vstr_t *line, const char *prompt) { ...@@ -86,10 +89,10 @@ int readline(vstr_t *line, const char *prompt) {
} else if (c == '\r') { } else if (c == '\r') {
// newline // newline
stdout_tx_str("\r\n"); stdout_tx_str("\r\n");
if (line->len > orig_line_len && (MP_STATE_PORT(readline_hist)[0] == NULL || strcmp(MP_STATE_PORT(readline_hist)[0], line->buf + orig_line_len) != 0)) { if (rl.line->len > rl.orig_line_len && (MP_STATE_PORT(readline_hist)[0] == NULL || strcmp(MP_STATE_PORT(readline_hist)[0], rl.line->buf + rl.orig_line_len) != 0)) {
// a line which is not empty and different from the last one // a line which is not empty and different from the last one
// so update the history // so update the history
char *most_recent_hist = str_dup_maybe(line->buf + orig_line_len); char *most_recent_hist = str_dup_maybe(rl.line->buf + rl.orig_line_len);
if (most_recent_hist != NULL) { if (most_recent_hist != NULL) {
for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) { for (int i = READLINE_HIST_SIZE - 1; i > 0; i--) {
MP_STATE_PORT(readline_hist)[i] = MP_STATE_PORT(readline_hist)[i - 1]; MP_STATE_PORT(readline_hist)[i] = MP_STATE_PORT(readline_hist)[i - 1];
...@@ -100,76 +103,76 @@ int readline(vstr_t *line, const char *prompt) { ...@@ -100,76 +103,76 @@ int readline(vstr_t *line, const char *prompt) {
return 0; return 0;
} else if (c == 27) { } else if (c == 27) {
// escape sequence // escape sequence
escape_seq = ESEQ_ESC; rl.escape_seq = ESEQ_ESC;
} else if (c == 8 || c == 127) { } else if (c == 8 || c == 127) {
// backspace/delete // backspace/delete
if (cursor_pos > orig_line_len) { if (rl.cursor_pos > rl.orig_line_len) {
vstr_cut_out_bytes(line, cursor_pos - 1, 1); vstr_cut_out_bytes(rl.line, rl.cursor_pos - 1, 1);
// set redraw parameters // set redraw parameters
redraw_step_back = 1; redraw_step_back = 1;
redraw_from_cursor = true; redraw_from_cursor = true;
} }
} else if (32 <= c && c <= 126) { } else if (32 <= c && c <= 126) {
// printable character // printable character
vstr_ins_char(line, cursor_pos, c); vstr_ins_char(rl.line, rl.cursor_pos, c);
// set redraw parameters // set redraw parameters
redraw_from_cursor = true; redraw_from_cursor = true;
redraw_step_forward = 1; redraw_step_forward = 1;
} }
} else if (escape_seq == ESEQ_ESC) { } else if (rl.escape_seq == ESEQ_ESC) {
switch (c) { switch (c) {
case '[': case '[':
escape_seq = ESEQ_ESC_BRACKET; rl.escape_seq = ESEQ_ESC_BRACKET;
break; break;
case 'O': case 'O':
escape_seq = ESEQ_ESC_O; rl.escape_seq = ESEQ_ESC_O;
break; break;
default: default:
DEBUG_printf("(ESC %d)", c); DEBUG_printf("(ESC %d)", c);
escape_seq = ESEQ_NONE; rl.escape_seq = ESEQ_NONE;
} }
} else if (escape_seq == ESEQ_ESC_BRACKET) { } else if (rl.escape_seq == ESEQ_ESC_BRACKET) {
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9') {
escape_seq = ESEQ_ESC_BRACKET_DIGIT; rl.escape_seq = ESEQ_ESC_BRACKET_DIGIT;
escape_seq_buf[0] = c; rl.escape_seq_buf[0] = c;
} else { } else {
escape_seq = ESEQ_NONE; rl.escape_seq = ESEQ_NONE;
if (c == 'A') { if (c == 'A') {
// up arrow // up arrow
if (hist_cur + 1 < READLINE_HIST_SIZE && MP_STATE_PORT(readline_hist)[hist_cur + 1] != NULL) { if (rl.hist_cur + 1 < READLINE_HIST_SIZE && MP_STATE_PORT(readline_hist)[rl.hist_cur + 1] != NULL) {
// increase hist num // increase hist num
hist_cur += 1; rl.hist_cur += 1;
// set line to history // set line to history
line->len = orig_line_len; rl.line->len = rl.orig_line_len;
vstr_add_str(line, MP_STATE_PORT(readline_hist)[hist_cur]); vstr_add_str(rl.line, MP_STATE_PORT(readline_hist)[rl.hist_cur]);
// set redraw parameters // set redraw parameters
redraw_step_back = cursor_pos - orig_line_len; redraw_step_back = rl.cursor_pos - rl.orig_line_len;
redraw_from_cursor = true; redraw_from_cursor = true;
redraw_step_forward = line->len - orig_line_len; redraw_step_forward = rl.line->len - rl.orig_line_len;
} }
} else if (c == 'B') { } else if (c == 'B') {
// down arrow // down arrow
if (hist_cur >= 0) { if (rl.hist_cur >= 0) {
// decrease hist num // decrease hist num
hist_cur -= 1; rl.hist_cur -= 1;
// set line to history // set line to history
vstr_cut_tail_bytes(line, line->len - orig_line_len); vstr_cut_tail_bytes(rl.line, rl.line->len - rl.orig_line_len);
if (hist_cur >= 0) { if (rl.hist_cur >= 0) {
vstr_add_str(line, MP_STATE_PORT(readline_hist)[hist_cur]); vstr_add_str(rl.line, MP_STATE_PORT(readline_hist)[rl.hist_cur]);
} }
// set redraw parameters // set redraw parameters
redraw_step_back = cursor_pos - orig_line_len; redraw_step_back = rl.cursor_pos - rl.orig_line_len;
redraw_from_cursor = true; redraw_from_cursor = true;
redraw_step_forward = line->len - orig_line_len; redraw_step_forward = rl.line->len - rl.orig_line_len;
} }
} else if (c == 'C') { } else if (c == 'C') {
// right arrow // right arrow
if (cursor_pos < line->len) { if (rl.cursor_pos < rl.line->len) {
redraw_step_forward = 1; redraw_step_forward = 1;
} }
} else if (c == 'D') { } else if (c == 'D') {
// left arrow // left arrow
if (cursor_pos > orig_line_len) { if (rl.cursor_pos > rl.orig_line_len) {
redraw_step_back = 1; redraw_step_back = 1;
} }
} else if (c == 'H') { } else if (c == 'H') {
...@@ -182,22 +185,22 @@ int readline(vstr_t *line, const char *prompt) { ...@@ -182,22 +185,22 @@ int readline(vstr_t *line, const char *prompt) {
DEBUG_printf("(ESC [ %d)", c); DEBUG_printf("(ESC [ %d)", c);
} }
} }
} else if (escape_seq == ESEQ_ESC_BRACKET_DIGIT) { } else if (rl.escape_seq == ESEQ_ESC_BRACKET_DIGIT) {
if (c == '~') { if (c == '~') {
if (escape_seq_buf[0] == '1' || escape_seq_buf[0] == '7') { if (rl.escape_seq_buf[0] == '1' || rl.escape_seq_buf[0] == '7') {
home_key: home_key:
redraw_step_back = cursor_pos - orig_line_len; redraw_step_back = rl.cursor_pos - rl.orig_line_len;
} else if (escape_seq_buf[0] == '4' || escape_seq_buf[0] == '8') { } else if (rl.escape_seq_buf[0] == '4' || rl.escape_seq_buf[0] == '8') {
end_key: end_key:
redraw_step_forward = line->len - cursor_pos; redraw_step_forward = rl.line->len - rl.cursor_pos;
} else { } else {
DEBUG_printf("(ESC [ %c %d)", escape_seq_buf[0], c); DEBUG_printf("(ESC [ %c %d)", rl.escape_seq_buf[0], c);
} }
} else { } else {
DEBUG_printf("(ESC [ %c %d)", escape_seq_buf[0], c); DEBUG_printf("(ESC [ %c %d)", rl.escape_seq_buf[0], c);
} }
escape_seq = ESEQ_NONE; rl.escape_seq = ESEQ_NONE;
} else if (escape_seq == ESEQ_ESC_O) { } else if (rl.escape_seq == ESEQ_ESC_O) {
switch (c) { switch (c) {
case 'H': case 'H':
goto home_key; goto home_key;
...@@ -205,10 +208,10 @@ end_key: ...@@ -205,10 +208,10 @@ end_key:
goto end_key; goto end_key;
default: default:
DEBUG_printf("(ESC O %d)", c); DEBUG_printf("(ESC O %d)", c);
escape_seq = ESEQ_NONE; rl.escape_seq = ESEQ_NONE;
} }
} else { } else {
escape_seq = ESEQ_NONE; rl.escape_seq = ESEQ_NONE;
} }
// redraw command prompt, efficiently // redraw command prompt, efficiently
...@@ -217,30 +220,57 @@ end_key: ...@@ -217,30 +220,57 @@ end_key:
for (int i = 0; i < redraw_step_back; i++) { for (int i = 0; i < redraw_step_back; i++) {
stdout_tx_str("\b"); stdout_tx_str("\b");
} }
cursor_pos -= redraw_step_back; rl.cursor_pos -= redraw_step_back;
} }
if (redraw_from_cursor) { if (redraw_from_cursor) {
if (line->len < last_line_len) { if (rl.line->len < last_line_len) {
// erase old chars // erase old chars
for (int i = cursor_pos; i < last_line_len; i++) { for (int i = rl.cursor_pos; i < last_line_len; i++) {
stdout_tx_str(" "); stdout_tx_str(" ");
} }
// step back // step back
for (int i = cursor_pos; i < last_line_len; i++) { for (int i = rl.cursor_pos; i < last_line_len; i++) {
stdout_tx_str("\b"); stdout_tx_str("\b");
} }
} }
// draw new chars // draw new chars
stdout_tx_strn(line->buf + cursor_pos, line->len - cursor_pos); stdout_tx_strn(rl.line->buf + rl.cursor_pos, rl.line->len - rl.cursor_pos);
// move cursor forward if needed (already moved forward by length of line, so move it back) // 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++) { for (int i = rl.cursor_pos + redraw_step_forward; i < rl.line->len; i++) {
stdout_tx_str("\b"); stdout_tx_str("\b");
} }
cursor_pos += redraw_step_forward; rl.cursor_pos += redraw_step_forward;
} else if (redraw_step_forward > 0) { } else if (redraw_step_forward > 0) {
// draw over old chars to move cursor forwards // draw over old chars to move cursor forwards
stdout_tx_strn(line->buf + cursor_pos, redraw_step_forward); stdout_tx_strn(rl.line->buf + rl.cursor_pos, redraw_step_forward);
cursor_pos += redraw_step_forward; rl.cursor_pos += redraw_step_forward;
}
return -1;
}
void readline_note_newline() {
rl.orig_line_len = rl.line->len;
rl.cursor_pos = rl.orig_line_len;
}
void readline_init(vstr_t *line) {
rl.line = line;
rl.orig_line_len = line->len;
rl.escape_seq = ESEQ_NONE;
rl.escape_seq_buf[0] = 0;
rl.hist_cur = -1;
rl.cursor_pos = rl.orig_line_len;
}
int readline(vstr_t *line, const char *prompt) {
stdout_tx_str(prompt);
readline_init(line);
for (;;) {
int c = stdin_rx_chr();
int r = readline_process_char(c);
if (r >= 0) {
return r;
} }
} }
} }
...@@ -32,3 +32,7 @@ ...@@ -32,3 +32,7 @@
void readline_init0(void); void readline_init0(void);
int readline(vstr_t *line, const char *prompt); int readline(vstr_t *line, const char *prompt);
void readline_init(vstr_t *line);
void readline_note_newline();
int readline_process_char(int c);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment