From 46a11028521425e7d6c85458c849bb96ff82152e Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 9 Oct 2015 22:41:10 +0100 Subject: [PATCH] repl: Add paste mode to friendly REPL, entered via CTRL-E. Use CTRL-E to enter paste mode. Prompt starts with "===" and accepts all characters verbatim, echoing them back. Only control characters are CTRL-C which cancels the input and returns to normal REPL, and CTRL-D which ends the input and executes it. The input is executed as though it were a file. The input is not added to the prompt history. --- lib/mp-readline/readline.c | 2 +- stmhal/pyexec.c | 53 +++++++++++++++++++++++++-------- unix/main.c | 61 +++++++++++++++++++++++++++----------- 3 files changed, 85 insertions(+), 31 deletions(-) diff --git a/lib/mp-readline/readline.c b/lib/mp-readline/readline.c index d0449e97ea..31b60e2682 100644 --- a/lib/mp-readline/readline.c +++ b/lib/mp-readline/readline.c @@ -105,7 +105,7 @@ int readline_process_char(int c) { bool redraw_from_cursor = false; int redraw_step_forward = 0; if (rl.escape_seq == ESEQ_NONE) { - if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && vstr_len(rl.line) == rl.orig_line_len) { + if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_E && vstr_len(rl.line) == rl.orig_line_len) { // control character with empty line return c; } else if (c == CHAR_CTRL_A) { diff --git a/stmhal/pyexec.c b/stmhal/pyexec.c index c692933dc0..a809940a8a 100644 --- a/stmhal/pyexec.c +++ b/stmhal/pyexec.c @@ -389,6 +389,7 @@ friendly_repl_reset: vstr_reset(&line); int ret = readline(&line, ">>> "); + mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; if (ret == CHAR_CTRL_A) { // change to raw REPL @@ -409,20 +410,46 @@ friendly_repl_reset: mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); return PYEXEC_FORCED_EXIT; + } else if (ret == CHAR_CTRL_E) { + // paste mode + mp_hal_stdout_tx_str("\r\npaste mode; CTRL-C to cancel, CTRL-D to finish\r\n=== "); + vstr_reset(&line); + for (;;) { + char c = mp_hal_stdin_rx_chr(); + if (c == CHAR_CTRL_C) { + // cancel everything + mp_hal_stdout_tx_str("\r\n"); + goto input_restart; + } else if (c == CHAR_CTRL_D) { + // end of input + mp_hal_stdout_tx_str("\r\n"); + break; + } else { + // add char to buffer and echo + vstr_add_byte(&line, c); + if (c == '\r') { + mp_hal_stdout_tx_str("\r\n=== "); + } else { + mp_hal_stdout_tx_strn(&c, 1); + } + } + } + parse_input_kind = MP_PARSE_FILE_INPUT; } else if (vstr_len(&line) == 0) { continue; - } - - while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { - vstr_add_byte(&line, '\n'); - ret = readline(&line, "... "); - if (ret == CHAR_CTRL_C) { - // cancel everything - mp_hal_stdout_tx_str("\r\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // stop entering compound statement - break; + } else { + // got a line with non-zero length, see if it needs continuing + while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { + vstr_add_byte(&line, '\n'); + ret = readline(&line, "... "); + if (ret == CHAR_CTRL_C) { + // cancel everything + mp_hal_stdout_tx_str("\r\n"); + goto input_restart; + } else if (ret == CHAR_CTRL_D) { + // stop entering compound statement + break; + } } } @@ -430,7 +457,7 @@ friendly_repl_reset: if (lex == NULL) { printf("MemoryError\n"); } else { - ret = parse_compile_execute(lex, MP_PARSE_SINGLE_INPUT, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL); + ret = parse_compile_execute(lex, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL); if (ret & PYEXEC_FORCED_EXIT) { return ret; } diff --git a/unix/main.c b/unix/main.c index 7a2f7e4ff1..a3f4cfe6cf 100644 --- a/unix/main.c +++ b/unix/main.c @@ -162,10 +162,12 @@ STATIC int do_repl(void) { vstr_t line; vstr_init(&line, 16); for (;;) { + mp_hal_stdio_mode_raw(); + input_restart: vstr_reset(&line); - mp_hal_stdio_mode_raw(); int ret = readline(&line, ">>> "); + mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; if (ret == CHAR_CTRL_D) { // EOF @@ -173,31 +175,56 @@ STATIC int do_repl(void) { mp_hal_stdio_mode_orig(); vstr_clear(&line); return 0; + } else if (ret == CHAR_CTRL_E) { + // paste mode + mp_hal_stdout_tx_str("\npaste mode; CTRL-C to cancel, CTRL-D to finish\n=== "); + vstr_reset(&line); + for (;;) { + char c = mp_hal_stdin_rx_chr(); + if (c == CHAR_CTRL_C) { + // cancel everything + mp_hal_stdout_tx_str("\n"); + goto input_restart; + } else if (c == CHAR_CTRL_D) { + // end of input + mp_hal_stdout_tx_str("\n"); + break; + } else { + // add char to buffer and echo + vstr_add_byte(&line, c); + if (c == '\r') { + mp_hal_stdout_tx_str("\n=== "); + } else { + mp_hal_stdout_tx_strn(&c, 1); + } + } + } + parse_input_kind = MP_PARSE_FILE_INPUT; } else if (line.len == 0) { if (ret != 0) { printf("\n"); } - mp_hal_stdio_mode_orig(); - continue; - } - - while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { - vstr_add_byte(&line, '\n'); - ret = readline(&line, "... "); - if (ret == CHAR_CTRL_C) { - // cancel everything - printf("\n"); - mp_hal_stdio_mode_orig(); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // stop entering compound statement - break; + goto input_restart; + } else { + // got a line with non-zero length, see if it needs continuing + while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { + vstr_add_byte(&line, '\n'); + ret = readline(&line, "... "); + if (ret == CHAR_CTRL_C) { + // cancel everything + printf("\n"); + goto input_restart; + } else if (ret == CHAR_CTRL_D) { + // stop entering compound statement + break; + } } } + mp_hal_stdio_mode_orig(); mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line.buf, line.len, false); - ret = execute_from_lexer(lex, MP_PARSE_SINGLE_INPUT, true); + ret = execute_from_lexer(lex, parse_input_kind, true); if (ret & FORCED_EXIT) { return ret; }