Specify multiple binds on a single line

This commit is contained in:
Harry Jeffery 2018-11-26 21:32:27 +00:00
parent 2fa568c628
commit c0a7723905
2 changed files with 86 additions and 23 deletions

View file

@ -71,7 +71,7 @@ The *[options]* section accepts the following settings:
*stay_fullscreen_on_focus_loss* = <true|false>::
Stay full screen even when imv loses focus. Defaults to 'false'.
*suppess_default_binds* = <true|false>::
*suppress_default_binds* = <true|false>::
Disable imv's built-in binds so they don't conflict with custom ones.
Defaults to 'false'.
@ -96,8 +96,10 @@ Binds
The *[binds]* section allows custom key bindings to be added to imv.
Binds are in the format 'key combination = command'. A key combination can
consist of multiple keys in succession. If there is more then one command
defined for a given key combination, they are executed one after another.
consist of multiple keys in succession. Multiple commands for a single key
combination can be defined by separating each command with a ';'. Single and
double quotes are honoured, as is escaping with a backslash, to allow the
proper quoting of shell commands.
Single keys such as 'q' are just that: 'q = quit' will bind the 'q' key to the
'quit' command.

101
src/imv.c
View file

@ -121,27 +121,93 @@ static void render_window(struct imv *imv);
static void update_env_vars(struct imv *imv);
static size_t generate_env_text(struct imv *imv, char *buf, size_t len, const char *format);
static const char *add_bind(struct imv *imv, const char *keys, const char *command)
/* Finds the next split between commands in a string ';', and provies it as
* out with a len. Returns the next starting point after the current string,
* or NULL if nothing left.
*/
static const char *split_commands(const char *start, const char **out, size_t *len)
{
bool in_single_quotes = false;
bool in_double_quotes = false;
const char *str = start;
while (*str) {
if (!in_single_quotes && *str == '"') {
in_double_quotes = !in_double_quotes;
} else if (!in_double_quotes && *str == '\'') {
in_single_quotes = !in_single_quotes;
} else if (*str == '\\') {
/* We don't care about the behaviour of any escaped character, just
* make sure to skip over them. We do need to make sure not to allow
* escaping of the null terminator though.
*/
if (str[1] != '\0') {
++str;
}
} else if (!in_single_quotes && !in_double_quotes && *str == ';') {
/* Found a command split that wasn't escaped or quoted */
*out = start;
*len = str - start;
return str + 1;
}
++str;
}
*out = start;
*len = str - start;
return str;
}
static bool add_bind(struct imv *imv, const char *keys, const char *commands)
{
struct list *list = imv_bind_parse_keys(keys);
if(!list) {
return "Invalid key combination";
fprintf(stderr, "Invalid key combination");
return false;
}
char command_buf[512];
const char *command_ptr;
size_t command_len;
bool success = true;
imv_binds_clear_key(imv->binds, list);
while (*commands) {
commands = split_commands(commands, &command_ptr, &command_len);
if (!command_ptr) {
break;
}
strncpy(&command_buf[0], command_ptr, sizeof command_buf);
if (command_len >= sizeof command_buf) {
fprintf(stderr, "Command exceeded max length, not binding: %s\n", &command_buf[0]);
continue;
}
command_buf[command_len] = '\0';
enum bind_result result = imv_binds_add(imv->binds, list, &command_buf[0]);
if (result == BIND_INVALID_KEYS) {
fprintf(stderr, "Invalid keys to bind to");
success = false;
break;
} else if (result == BIND_INVALID_COMMAND) {
fprintf(stderr, "No command given to bind to");
success = false;
break;
} else if (result == BIND_CONFLICTS) {
fprintf(stderr, "Key combination conflicts with existing bind");
success = false;
break;
}
}
enum bind_result result = imv_binds_add(imv->binds, list, command);
list_free(list);
if (result == BIND_SUCCESS) {
return NULL;
} else if (result == BIND_INVALID_KEYS) {
return "Invalid keys to bind to";
} else if (result == BIND_INVALID_COMMAND) {
return "No command given to bind to";
} else if (result == BIND_CONFLICTS) {
return "Key combination conflicts with existing bind";
}
return NULL;
return success;
}
struct imv *imv_create(void)
@ -962,12 +1028,7 @@ static int handle_ini_value(void *user, const char *section, const char *name,
struct imv *imv = user;
if (!strcmp(section, "binds")) {
const char *err = add_bind(imv, name, value);
if (err) {
fprintf(stderr, "Config error: %s\n", err);
return 0;
}
return 1;
return add_bind(imv, name, value);
}
if (!strcmp(section, "aliases")) {