[PATCH] input: interpret commands of the form {functionname} inside string binds

Benno Schulenberg <[email protected]>
Newsgroups gmane.editors.nano.devel
Message-ID <[email protected]>
This allows specifying bindable functions in a string bind by name
instead of by the literal control code or escape sequence to which
they are bound, which makes for a much more readable string bind,
and also allows specifying functions that are not bound to any key.

The opening brace, {, is made into a special symbol inside a string
bind, and each literal occurrence there needs to be escaped as {{}.

This fulfills https://savannah.gnu.org/bugs/?61692.
Requested-by: Tasos Papastylianou <[email protected]>

Original-idea-by: Brand Huntsman <[email protected]>
  https://lists.gnu.org/archive/html/nano-devel/2018-02/msg00006.html
---
 src/definitions.h | 12 +++++++++
 src/global.c      |  7 ++++++
 src/nano.c        |  4 +++
 src/prototypes.h  |  4 +++
 src/winio.c       | 63 +++++++++++++++++++++++++++++++++++++++++++----
 5 files changed, 85 insertions(+), 5 deletions(-)

diff --git a/src/definitions.h b/src/definitions.h
index 58012553..b064891a 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -214,6 +214,18 @@
 #define SHIFT_DELETE    0x45D
 #define SHIFT_TAB       0x45F
 
+/* A special keycode for when a string bind has been partially implanted. */
+#define MORE_PLANTS       0x4EA
+
+/* A special keycode for when a string bind has an unpaired opening brace. */
+#define MISSING_BRACE     0x4EB
+
+/* A special keycode for when a function in a string bind needs execution. */
+#define PLANTED_COMMAND   0x4EC
+
+/* A special keycode for when a function name in a string bind is invalid. */
+#define NO_SUCH_FUNCTION  0x4EF
+
 /* A special keycode for when <Tab> is pressed while the mark is on. */
 #define INDENT_KEY  0x4F1
 
diff --git a/src/global.c b/src/global.c
index 80adf6e9..df88c92d 100644
--- a/src/global.c
+++ b/src/global.c
@@ -266,6 +266,9 @@ char *startup_problem = NULL;
 #endif
 #ifdef ENABLE_NANORC
 char *custom_nanorc = NULL;
+
+char *commandname = NULL;
+keystruct *planted_shortcut = NULL;
 #endif
 
 bool spotlighted = FALSE;
@@ -466,6 +469,10 @@ const keystruct *get_shortcut(int *keycode)
 	if (bracketed_paste && *keycode != BRACKETED_PASTE_MARKER)
 		return NULL;
 #endif
+#ifdef ENABLE_NANORC
+	if (*keycode == PLANTED_COMMAND)
+		return planted_shortcut;
+#endif
 
 	for (keystruct *sc = sclist; sc != NULL; sc = sc->next) {
 		if ((sc->menus & currmenu) && *keycode == sc->keycode)
diff --git a/src/nano.c b/src/nano.c
index fe139486..1fe0f1f1 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -1267,6 +1267,10 @@ void unbound_key(int code)
 		/* TRANSLATORS: This refers to a sequence of escape codes
 		 * (from the keyboard) that nano does not recognize. */
 		statusline(AHEM, _("Unknown sequence"));
+	else if (code == MISSING_BRACE)
+		statusline(AHEM, _("Missing }"));
+	else if (code == NO_SUCH_FUNCTION)
+		statusline(AHEM, _("No such function: %s"), commandname);
 #ifndef NANO_TINY
 	else if (code > KEY_F0 && code < KEY_F0 + 25)
 		/* TRANSLATORS: This refers to an unbound function key. */
diff --git a/src/prototypes.h b/src/prototypes.h
index 473779ed..dbe8dad8 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -181,6 +181,9 @@ extern char *startup_problem;
 #endif
 #ifdef ENABLE_NANORC
 extern char *custom_nanorc;
+
+extern char *commandname;
+extern keystruct *planted_shortcut;
 #endif
 
 extern bool spotlighted;
@@ -441,6 +444,7 @@ void display_rcfile_errors(void);
 void jot_error(const char *msg, ...);
 #endif
 #ifdef ENABLE_NANORC
+keystruct *strtosc(const char *input);
 #ifdef ENABLE_COLOR
 void parse_one_include(char *file, syntaxtype *syntax);
 void grab_and_store(const char *kind, char *ptr, regexlisttype **storage);
diff --git a/src/winio.c b/src/winio.c
index e7dce7ff..98c4b4bc 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -64,6 +64,8 @@ static bool has_more = FALSE;
 		/* Whether the current line has more text after the displayed part. */
 static bool is_shorter = TRUE;
 		/* Whether a row's text is narrower than the screen's width. */
+static const char *plants_pointer = NULL;
+		/* Points into the expansion string for the current implantation. */
 #ifndef NANO_TINY
 static size_t sequel_column = 0;
 		/* The starting column of the next chunk when softwrapping. */
@@ -326,14 +328,60 @@ void put_back(int keycode)
 }
 
 #ifdef ENABLE_NANORC
-/* Insert the given string into the keyboard buffer. */
+/* Set up the given expansion string to be ingested by the keyboard routines. */
 void implant(const char *string)
 {
-	for (int i = strlen(string); i > 0; i--)
-		put_back((unsigned char)string[i - 1]);
+	plants_pointer = string;
+	put_back(MORE_PLANTS);
 
 	mute_modifiers = TRUE;
 }
+
+/* Continue processing an expansion string.  Returns either an error code,
+ * a plain keycode, or a placeholder for a command shortcut. */
+int get_code_from_plantation(void)
+{
+	if (*plants_pointer == '{') {
+		char *closing = strchr(plants_pointer + 1, '}');
+
+		if (!closing)
+			return MISSING_BRACE;
+
+		if (plants_pointer[1] == '{' && plants_pointer[2] == '}') {
+			plants_pointer += 3;
+			if (*plants_pointer != '\0')
+				put_back(MORE_PLANTS);
+			return '{';
+		}
+
+		free(commandname);
+		free(planted_shortcut);
+
+		commandname = measured_copy(plants_pointer + 1, closing - plants_pointer - 1);
+		planted_shortcut = strtosc(commandname);
+
+		if (planted_shortcut) {
+			plants_pointer = closing + 1;
+			if (*plants_pointer != '\0')
+				put_back(MORE_PLANTS);
+			return PLANTED_COMMAND;
+		} else
+			return NO_SUCH_FUNCTION;
+	} else {
+		char *opening = strchr(plants_pointer, '{');
+		int length = (opening ? opening - plants_pointer : strlen(plants_pointer));
+
+		if (opening)
+			put_back(MORE_PLANTS);
+
+		for (int index = length - 1; index >= 0; index--)
+			put_back((unsigned char)plants_pointer[index]);
+
+		plants_pointer += length;
+
+		return ERR;
+	}
+}
 #endif
 
 /* Return one code from the keystroke buffer.  If the buffer is empty
@@ -347,7 +395,11 @@ int get_input(WINDOW *frame)
 
 	if (waiting_codes > 0) {
 		waiting_codes--;
-		return *(nextcodes++);
+		if (*nextcodes == MORE_PLANTS) {
+			nextcodes++;
+			return get_code_from_plantation();
+		} else
+			return *(nextcodes++);
 	} else
 		return ERR;
 }
@@ -940,7 +992,8 @@ int parse_kbinput(WINDOW *frame)
 		} else if (++escapes > 2)
 			escapes = (last_escape_was_alone ? 0 : 1);
 		return ERR;
-	}
+	} else if (keycode == ERR)
+		return ERR;
 
 	if (escapes == 0) {
 		/* Most key codes in byte range cannot be special keys. */
-- 
2.35.3
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.