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

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 <tpapastylianou@hotmail.com>

Original-idea-by: Brand Huntsman <alpha@qzx.com>
  https://lists.gnu.org/archive/html/nano-devel/2018-02/msg00006.html
This commit is contained in:
Benno Schulenberg 2022-08-09 17:04:18 +02:00
parent 3a781fd719
commit 958ec294b2
5 changed files with 85 additions and 5 deletions

View File

@ -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

View File

@ -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;
@ -460,6 +463,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)

View File

@ -1269,6 +1269,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. */

View File

@ -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;
@ -440,6 +443,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);

View File

@ -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. */