From 501d05c5d1d72513284b6d4ac2563c2bdee27fbd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marco=20Diego=20Aur=C3=A9lio=20Mesquita?=
 <marcodiegomesquita@gmail.com>
Date: Mon, 9 Oct 2017 19:03:51 -0300
Subject: [PATCH] new feature: the ability to record and play back a series of
 keystrokes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Allow the user to record and run a single macro.  The default binding
for starting and stopping the recording is M-: (Alt + colon) and for
running the macro M-; (Alt + semicolon).

This fulfills https://savannah.gnu.org/bugs/?50314.
Requested-by: Peter Passchier <peter@passchier.net>

Signed-off-by: Marco Diego Aurélio Mesquita <marcodiegomesquita@gmail.com>
Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
---
 src/global.c | 13 +++++++++++
 src/proto.h  |  2 ++
 src/winio.c  | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)

diff --git a/src/global.c b/src/global.c
index ebfe2242..7085263c 100644
--- a/src/global.c
+++ b/src/global.c
@@ -634,6 +634,8 @@ void shortcut_init(void)
     const char *nano_savefile_msg = N_("Save file without prompting");
     const char *nano_findprev_msg = N_("Search next occurrence backward");
     const char *nano_findnext_msg = N_("Search next occurrence forward");
+    const char *nano_recordmacro_msg = N_("Start/stop recording a macro");
+    const char *nano_runmacro_msg = N_("Run the last recorded macro");
 #endif
     const char *nano_case_msg =
 	N_("Toggle the case sensitivity of the search");
@@ -976,6 +978,11 @@ void shortcut_init(void)
 	N_("Comment Lines"), IFSCHELP(nano_comment_msg), BLANKAFTER, NOVIEW);
 #endif
 #ifndef NANO_TINY
+    add_to_funcs(record_macro, MMAIN,
+	N_("Record"), IFSCHELP(nano_recordmacro_msg), TOGETHER, VIEW);
+    add_to_funcs(run_macro, MMAIN,
+	N_("Run Macro"), IFSCHELP(nano_runmacro_msg), BLANKAFTER, VIEW);
+
     add_to_funcs(do_search_backward, MMAIN,
 	N_("Where Was"), IFSCHELP(N_(nano_wherewas_msg)), BLANKAFTER, VIEW);
 
@@ -1140,6 +1147,8 @@ void shortcut_init(void)
     add_to_sclist(MMAIN, "M-^", 0, do_copy_text, 0);
     add_to_sclist(MMAIN, "M-}", 0, do_indent, 0);
     add_to_sclist(MMAIN, "M-{", 0, do_unindent, 0);
+    add_to_sclist(MMAIN, "M-:", 0, record_macro, 0);
+    add_to_sclist(MMAIN, "M-;", 0, run_macro, 0);
     add_to_sclist(MMAIN, "M-U", 0, do_undo, 0);
     add_to_sclist(MMAIN, "M-E", 0, do_redo, 0);
 #endif
@@ -1543,6 +1552,10 @@ sc *strtosc(const char *input)
 	s->scfunc = do_find_bracket;
     else if (!strcasecmp(input, "wordcount"))
 	s->scfunc = do_wordlinechar_count;
+    else if (!strcasecmp(input, "recordmacro"))
+	s->scfunc = record_macro;
+    else if (!strcasecmp(input, "runmacro"))
+	s->scfunc = run_macro;
     else if (!strcasecmp(input, "undo"))
 	s->scfunc = do_undo;
     else if (!strcasecmp(input, "redo"))
diff --git a/src/proto.h b/src/proto.h
index 617a2da3..2bc7fe7a 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -627,6 +627,8 @@ void dump_filestruct_reverse(void);
 #endif
 
 /* Most functions in winio.c. */
+void record_macro(void);
+void run_macro(void);
 void get_key_buffer(WINDOW *win);
 size_t get_key_buffer_len(void);
 void unget_kbinput(int kbinput, bool metakey);
diff --git a/src/winio.c b/src/winio.c
index f90d23dc..dd717b4c 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -51,6 +51,67 @@ static bool seen_wide = FALSE;
 #endif
 static bool reveal_cursor = FALSE;
 	/* Whether the cursor should be shown when waiting for input. */
+#ifndef NANO_TINY
+static bool recording = FALSE;
+	/* Whether we are in the process of recording a macro. */
+static int *macro_buffer = NULL;
+	/* A buffer where the recorded key codes are stored. */
+static size_t macro_length = 0;
+	/* The current length of the macro. */
+
+/* Add the given code to the macro buffer. */
+void add_to_macrobuffer(int code)
+{
+    macro_length++;
+    macro_buffer = (int*)nrealloc(macro_buffer, macro_length * sizeof(int));
+    macro_buffer[macro_length - 1] = code;
+}
+
+/* Remove the last key code plus any trailing Esc codes from macro buffer. */
+void snip_last_keystroke(void)
+{
+    macro_length--;
+    while (macro_length > 0 && macro_buffer[macro_length - 1] == '\x1b')
+	macro_length--;
+}
+
+/* Start or stop the recording of keystrokes. */
+void record_macro(void)
+{
+    recording = !recording;
+
+    if (recording) {
+	macro_length = 0;
+	statusbar(_("Recording a macro..."));
+    } else {
+	snip_last_keystroke();
+	statusbar(_("Stopped recording"));
+    }
+}
+
+/* Copy the stored sequence of codes into the regular key buffer,
+ * so they will be "executed" again. */
+void run_macro(void)
+{
+    size_t i;
+
+    if (recording) {
+	statusline(HUSH, _("Cannot run macro while recording"));
+	snip_last_keystroke();
+	return;
+    }
+
+    if (macro_length == 0)
+	return;
+
+    free(key_buffer);
+    key_buffer = (int *)nmalloc(macro_length * sizeof(int));
+    key_buffer_len = macro_length;
+
+    for (i = 0; i < macro_length; i++)
+	key_buffer[i] = macro_buffer[i];
+}
+#endif /* !NANO_TINY */
 
 /* Control character compatibility:
  *
@@ -167,6 +228,10 @@ void get_key_buffer(WINDOW *win)
     nodelay(win, TRUE);
 
     while (TRUE) {
+#ifndef NANO_TINY
+	if (recording)
+	    add_to_macrobuffer(input);
+#endif
 	input = wgetch(win);
 
 	/* If there aren't any more characters, stop reading. */
-- 
GitLab