From 0af73014cc61a05bcf53558d7c2d554d98da4419 Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Thu, 20 Aug 2015 10:34:16 +0100
Subject: [PATCH] lib/mp-readline: Add auto-indent support.

4 spaces are added at start of line to match previous indent, and if
previous line ended in colon.

Backspace deletes 4 space if only spaces begin a line.

Configurable via MICROPY_REPL_AUTO_INDENT.  Disabled by default.
---
 lib/mp-readline/readline.c | 60 ++++++++++++++++++++++++++++++++++++--
 py/mpconfig.h              |  5 ++++
 2 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/lib/mp-readline/readline.c b/lib/mp-readline/readline.c
index 8909c8be6..d0449e97e 100644
--- a/lib/mp-readline/readline.c
+++ b/lib/mp-readline/readline.c
@@ -160,9 +160,29 @@ int readline_process_char(int c) {
         } else if (c == 8 || c == 127) {
             // backspace/delete
             if (rl.cursor_pos > rl.orig_line_len) {
-                vstr_cut_out_bytes(rl.line, rl.cursor_pos - 1, 1);
+                // work out how many chars to backspace
+                #if MICROPY_REPL_AUTO_INDENT
+                int nspace = 0;
+                for (size_t i = rl.orig_line_len; i < rl.cursor_pos; i++) {
+                    if (rl.line->buf[i] != ' ') {
+                        nspace = 0;
+                        break;
+                    }
+                    nspace += 1;
+                }
+                if (nspace < 4) {
+                    nspace = 1;
+                } else {
+                    nspace = 4;
+                }
+                #else
+                int nspace = 1;
+                #endif
+
+                // do the backspace
+                vstr_cut_out_bytes(rl.line, rl.cursor_pos - nspace, nspace);
                 // set redraw parameters
-                redraw_step_back = 1;
+                redraw_step_back = nspace;
                 redraw_from_cursor = true;
             }
         #if MICROPY_HELPER_REPL
@@ -335,11 +355,44 @@ delete_key:
     return -1;
 }
 
+#if MICROPY_REPL_AUTO_INDENT
+STATIC void readline_auto_indent(void) {
+    vstr_t *line = rl.line;
+    if (line->len > 1 && line->buf[line->len - 1] == '\n') {
+        int i;
+        for (i = line->len - 1; i > 0; i--) {
+            if (line->buf[i - 1] == '\n') {
+                break;
+            }
+        }
+        size_t j;
+        for (j = i; j < line->len; j++) {
+            if (line->buf[j] != ' ') {
+                break;
+            }
+        }
+        // i=start of line; j=first non-space
+        int n = (j - i) / 4;
+        if (line->buf[line->len - 2] == ':') {
+            n += 1;
+        }
+        while (n-- > 0) {
+            vstr_add_strn(line, "    ", 4);
+            mp_hal_stdout_tx_strn("    ", 4);
+            rl.cursor_pos += 4;
+        }
+    }
+}
+#endif
+
 void readline_note_newline(const char *prompt) {
     rl.orig_line_len = rl.line->len;
     rl.cursor_pos = rl.orig_line_len;
     rl.prompt = prompt;
     mp_hal_stdout_tx_str(prompt);
+    #if MICROPY_REPL_AUTO_INDENT
+    readline_auto_indent();
+    #endif
 }
 
 void readline_init(vstr_t *line, const char *prompt) {
@@ -351,6 +404,9 @@ void readline_init(vstr_t *line, const char *prompt) {
     rl.cursor_pos = rl.orig_line_len;
     rl.prompt = prompt;
     mp_hal_stdout_tx_str(prompt);
+    #if MICROPY_REPL_AUTO_INDENT
+    readline_auto_indent();
+    #endif
 }
 
 int readline(vstr_t *line, const char *prompt) {
diff --git a/py/mpconfig.h b/py/mpconfig.h
index ce6fa6cea..eb5b7243f 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -302,6 +302,11 @@
 #define MICROPY_REPL_EMACS_KEYS (0)
 #endif
 
+// Whether to implement auto-indent in REPL
+#ifndef MICROPY_REPL_AUTO_INDENT
+#define MICROPY_REPL_AUTO_INDENT (0)
+#endif
+
 // Whether port requires event-driven REPL functions
 #ifndef MICROPY_REPL_EVENT_DRIVEN
 #define MICROPY_REPL_EVENT_DRIVEN (0)
-- 
GitLab