aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sam.c51
-rw-r--r--vis-cmds.c111
-rw-r--r--vis.c4
-rw-r--r--vis.h15
4 files changed, 127 insertions, 54 deletions
diff --git a/sam.c b/sam.c
index f21d1ae..b287585 100644
--- a/sam.c
+++ b/sam.c
@@ -267,18 +267,11 @@ static const CommandDef cmddef_select = {
/* :set command options */
typedef struct {
- const char *names[3]; /* name and optional alias */
- enum {
- OPTION_TYPE_STRING,
- OPTION_TYPE_BOOL,
- OPTION_TYPE_NUMBER,
- } type;
- enum {
- OPTION_FLAG_NONE = 0,
- OPTION_FLAG_OPTIONAL = 1 << 0, /* value is optional */
- OPTION_FLAG_WINDOW = 1 << 1, /* option requires an active window */
- } flags;
- VIS_HELP_DECL(const char *help;) /* short, one line help text */
+ const char *names[3]; /* name and optional alias */
+ enum VisOption flags; /* option type, etc. */
+ VIS_HELP_DECL(const char *help;) /* short, one line help text */
+ VisOptionFunction *func; /* option handler, NULL for bulitins */
+ void *context; /* context passed to option handler function */
} OptionDef;
enum {
@@ -304,87 +297,87 @@ enum {
static const OptionDef options[] = {
[OPTION_SHELL] = {
{ "shell" },
- OPTION_TYPE_STRING, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_STRING,
VIS_HELP("Shell to use for external commands (default: $SHELL, /etc/passwd, /bin/sh)")
},
[OPTION_ESCDELAY] = {
{ "escdelay" },
- OPTION_TYPE_NUMBER, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_NUMBER,
VIS_HELP("Miliseconds to wait to distinguish <Escape> from terminal escape sequences")
},
[OPTION_AUTOINDENT] = {
{ "autoindent", "ai" },
- OPTION_TYPE_BOOL, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_BOOL,
VIS_HELP("Copy leading white space from previous line")
},
[OPTION_EXPANDTAB] = {
{ "expandtab", "et" },
- OPTION_TYPE_BOOL, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_BOOL,
VIS_HELP("Replace entered <Tab> with `tabwidth` spaces")
},
[OPTION_TABWIDTH] = {
{ "tabwidth", "tw" },
- OPTION_TYPE_NUMBER, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_NUMBER,
VIS_HELP("Number of spaces to display (and insert if `expandtab` is enabled) for a tab")
},
[OPTION_THEME] = {
{ "theme" },
- OPTION_TYPE_STRING, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_STRING,
VIS_HELP("Color theme to use filename without extension")
},
[OPTION_SYNTAX] = {
{ "syntax" },
- OPTION_TYPE_STRING, OPTION_FLAG_WINDOW|OPTION_FLAG_OPTIONAL,
+ VIS_OPTION_TYPE_STRING|VIS_OPTION_VALUE_OPTIONAL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Syntax highlighting lexer to use filename without extension")
},
[OPTION_SHOW_SPACES] = {
{ "show-spaces" },
- OPTION_TYPE_BOOL, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_BOOL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Display replacement symbol instead of a space")
},
[OPTION_SHOW_TABS] = {
{ "show-tabs" },
- OPTION_TYPE_BOOL, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_BOOL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Display replacement symbol for tabs")
},
[OPTION_SHOW_NEWLINES] = {
{ "show-newlines" },
- OPTION_TYPE_BOOL, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_BOOL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Display replacement symbol for newlines")
},
[OPTION_NUMBER] = {
{ "numbers", "nu" },
- OPTION_TYPE_BOOL, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_BOOL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Display absolute line numbers")
},
[OPTION_NUMBER_RELATIVE] = {
{ "relativenumbers", "rnu" },
- OPTION_TYPE_BOOL, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_BOOL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Display relative line numbers")
},
[OPTION_CURSOR_LINE] = {
{ "cursorline", "cul" },
- OPTION_TYPE_BOOL, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_BOOL|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Highlight current cursor line")
},
[OPTION_COLOR_COLUMN] = {
{ "colorcolumn", "cc" },
- OPTION_TYPE_NUMBER, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_NUMBER|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Highlight a fixed column")
},
[OPTION_HORIZON] = {
{ "horizon" },
- OPTION_TYPE_NUMBER, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_NUMBER|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Number of bytes to consider for syntax highlighting")
},
[OPTION_SAVE_METHOD] = {
{ "savemethod" },
- OPTION_TYPE_STRING, OPTION_FLAG_WINDOW,
+ VIS_OPTION_TYPE_STRING|VIS_OPTION_NEED_WINDOW,
VIS_HELP("Save method to use for current file 'auto', 'atomic' or 'inplace'")
},
[OPTION_CHANGE_256COLORS] = {
{ "change-256colors" },
- OPTION_TYPE_BOOL, OPTION_FLAG_NONE,
+ VIS_OPTION_TYPE_BOOL,
VIS_HELP("Change 256 color palette to support 24bit colors")
},
};
diff --git a/vis-cmds.c b/vis-cmds.c
index f38a96f..812fbed 100644
--- a/vis-cmds.c
+++ b/vis-cmds.c
@@ -62,6 +62,60 @@ bool vis_cmd_unregister(Vis *vis, const char *name) {
return true;
}
+static void option_free(OptionDef *opt) {
+ if (!opt)
+ return;
+ for (size_t i = 0; i < LENGTH(options); i++) {
+ if (opt == &options[i])
+ return;
+ }
+
+ for (const char **name = opt->names; *name; name++)
+ free((char*)*name);
+ free(VIS_HELP_USE((char*)opt->help));
+ free(opt);
+}
+
+bool vis_option_register(Vis *vis, const char *names[], enum VisOption flags,
+ VisOptionFunction *func, void *context, const char *help) {
+ for (const char **name = names; *name; name++) {
+ if (map_get(vis->options, *name))
+ return false;
+ }
+ OptionDef *opt = calloc(1, sizeof *opt);
+ if (!opt)
+ return false;
+ for (size_t i = 0; i < LENGTH(opt->names)-1 && names[i]; i++) {
+ if (!(opt->names[i] = strdup(names[i])))
+ goto err;
+ }
+ opt->flags = flags;
+ opt->func = func;
+ opt->context = context;
+#if CONFIG_HELP
+ if (help && !(opt->help = strdup(help)))
+ goto err;
+#endif
+ for (const char **name = names; *name; name++)
+ map_put(vis->options, *name, opt);
+ return true;
+err:
+ option_free(opt);
+ return false;
+}
+
+bool vis_option_unregister(Vis *vis, const char *name) {
+ OptionDef *opt = map_get(vis->options, name);
+ if (!opt)
+ return false;
+ for (const char **alias = opt->names; *alias; alias++) {
+ if (!map_delete(vis->options, *alias))
+ return false;
+ }
+ option_free(opt);
+ return true;
+}
+
static bool cmd_user(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) {
CmdUser *user = map_get(vis->usercmds, argv[0]);
return user && user->func(vis, win, user->data, cmd->flags == '!', argv, cur, range);
@@ -117,13 +171,13 @@ static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor
return false;
}
- if (!win && (opt->flags & OPTION_FLAG_WINDOW)) {
+ if (!win && (opt->flags & VIS_OPTION_NEED_WINDOW)) {
vis_info_show(vis, "Need active window for `:set %s'", name);
return false;
}
if (toggle) {
- if (opt->type != OPTION_TYPE_BOOL) {
+ if (!(opt->flags & VIS_OPTION_TYPE_BOOL)) {
vis_info_show(vis, "Only boolean options can be toggled");
return false;
}
@@ -134,23 +188,20 @@ static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor
}
Arg arg;
- switch (opt->type) {
- case OPTION_TYPE_STRING:
- if (!(opt->flags & OPTION_FLAG_OPTIONAL) && !argv[2]) {
+ if (opt->flags & VIS_OPTION_TYPE_STRING) {
+ if (!(opt->flags & VIS_OPTION_VALUE_OPTIONAL) && !argv[2]) {
vis_info_show(vis, "Expecting string option value");
return false;
}
arg.s = argv[2];
- break;
- case OPTION_TYPE_BOOL:
+ } else if (opt->flags & VIS_OPTION_TYPE_BOOL) {
if (!argv[2]) {
arg.b = !toggle;
} else if (!parse_bool(argv[2], &arg.b)) {
vis_info_show(vis, "Expecting boolean option value not: `%s'", argv[2]);
return false;
}
- break;
- case OPTION_TYPE_NUMBER:
+ } else if (opt->flags & VIS_OPTION_TYPE_NUMBER) {
if (!argv[2]) {
vis_info_show(vis, "Expecting number");
return false;
@@ -174,12 +225,16 @@ static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor
return false;
}
arg.i = lval;
- break;
- default:
+ } else {
return false;
}
- size_t opt_index = opt - options;
+ size_t opt_index = 0;
+ for (; opt_index < LENGTH(options); opt_index++) {
+ if (opt == &options[opt_index])
+ break;
+ }
+
switch (opt_index) {
case OPTION_SHELL:
{
@@ -301,7 +356,9 @@ static bool cmd_set(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor
vis->change_colors = toggle ? !vis->change_colors : arg.b;
break;
default:
- return false;
+ if (!opt->func)
+ return false;
+ return opt->func(vis, win, opt->context, toggle, opt->flags, name, &arg);
}
return true;
@@ -596,6 +653,21 @@ static bool print_cmd(const char *key, void *value, void *data) {
return text_appendf(data, " %-30s %s\n", usage, help ? help : "");
}
+static bool print_option(const char *key, void *value, void *txt) {
+ char desc[256];
+ const OptionDef *opt = value;
+ const char *help = VIS_HELP_USE(opt->help);
+ if (strcmp(key, opt->names[0]))
+ return true;
+ snprintf(desc, sizeof desc, "%s%s%s%s%s",
+ opt->names[0],
+ opt->names[1] ? "|" : "",
+ opt->names[1] ? opt->names[1] : "",
+ opt->flags & VIS_OPTION_TYPE_BOOL ? " on|off" : "",
+ opt->flags & VIS_OPTION_TYPE_NUMBER ? " nn" : "");
+ return text_appendf(txt, " %-30s %s\n", desc, help ? help : "");
+}
+
static void print_symbolic_keys(Vis *vis, Text *txt) {
static const int keys[] = {
TERMKEY_SYM_BACKSPACE,
@@ -710,18 +782,7 @@ static bool cmd_help(Vis *vis, Win *win, Command *cmd, const char *argv[], Curso
}
text_appendf(txt, "\n :set command options\n\n");
- for (int i = 0; i < LENGTH(options); i++) {
- char names[256];
- const OptionDef *opt = &options[i];
- const char *help = VIS_HELP_USE(opt->help);
- snprintf(names, sizeof names, "%s%s%s%s%s",
- opt->names[0],
- opt->names[1] ? "|" : "",
- opt->names[1] ? opt->names[1] : "",
- opt->type == OPTION_TYPE_BOOL ? " on|off" : "",
- opt->type == OPTION_TYPE_NUMBER ? " nn" : "");
- text_appendf(txt, " %-30s %s\n", names, help ? help : "");
- }
+ map_iterate(vis->options, print_option, txt);
text_appendf(txt, "\n Key binding actions\n\n");
map_iterate(vis->actions, print_action, txt);
diff --git a/vis.c b/vis.c
index f25ec8f..8813500 100644
--- a/vis.c
+++ b/vis.c
@@ -754,6 +754,10 @@ void vis_free(Vis *vis) {
}
map_free(vis->usercmds);
map_free(vis->cmds);
+ if (vis->options) {
+ const char *name;
+ while (map_first(vis->options, &name) && vis_option_unregister(vis, name));
+ }
map_free(vis->options);
map_free(vis->actions);
map_free(vis->keymap);
diff --git a/vis.h b/vis.h
index 46123ea..0d25d91 100644
--- a/vis.h
+++ b/vis.h
@@ -482,6 +482,21 @@ typedef bool (CmdFunc)(Vis*, Win*, void *data, bool force,
* unique prefix of the given name is executed */
bool vis_cmd_register(Vis*, const char *name, const char *help, void *data, CmdFunc*);
bool vis_cmd_unregister(Vis*, const char *name);
+
+enum VisOption {
+ VIS_OPTION_TYPE_BOOL = 1 << 0,
+ VIS_OPTION_TYPE_STRING = 1 << 1,
+ VIS_OPTION_TYPE_NUMBER = 1 << 2,
+ VIS_OPTION_VALUE_OPTIONAL = 1 << 3,
+ VIS_OPTION_NEED_WINDOW = 1 << 4,
+};
+
+typedef bool (VisOptionFunction)(Vis*, Win*, void *context, bool toggle,
+ enum VisOption, const char *name, Arg *value);
+bool vis_option_register(Vis*, const char *names[], enum VisOption,
+ VisOptionFunction*, void *context, const char *help);
+bool vis_option_unregister(Vis*, const char *name);
+
/* execute any kind (:,?,/) of prompt command */
bool vis_prompt_cmd(Vis*, const char *cmd);