index 41d8123379eec382c30f9e7d9e9bb9c1f09892d2..0d16f767be7d2375875d26c68b75060834364401 100644 (file)
--- a/tig.c
+++ b/tig.c
@@ -107,6 +107,7 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
#define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
#define ID_COLS 8
+#define AUTHOR_COLS 19
#define MIN_VIEW_HEIGHT 4
return 0;
}
+#define enum_equals(entry, str, len) \
+ ((entry).namelen == (len) && !string_enum_compare((entry).name, str, len))
+
struct enum_map {
const char *name;
int namelen;
int i;
for (i = 0; i < map_size; i++)
- if (namelen == map[i].namelen &&
- !string_enum_compare(name, map[i].name, namelen)) {
+ if (enum_equals(map[i], name, namelen)) {
*value = map[i].value;
return TRUE;
}
return offset * eastwest;
}
+#define DATE_INFO \
+ DATE_(NO), \
+ DATE_(DEFAULT), \
+ DATE_(RELATIVE), \
+ DATE_(SHORT)
+
enum date {
- DATE_NONE = 0,
- DATE_DEFAULT,
- DATE_RELATIVE,
- DATE_SHORT
+#define DATE_(name) DATE_##name
+ DATE_INFO
+#undef DATE_
};
-static char *
+static const struct enum_map date_map[] = {
+#define DATE_(name) ENUM_MAP(#name, DATE_##name)
+ DATE_INFO
+#undef DATE_
+};
+
+static const char *
string_date(const time_t *time, enum date date)
{
static char buf[DATE_COLS + 1];
}
+#define AUTHOR_VALUES \
+ AUTHOR_(NO), \
+ AUTHOR_(FULL), \
+ AUTHOR_(ABBREVIATED)
+
+enum author {
+#define AUTHOR_(name) AUTHOR_##name
+ AUTHOR_VALUES,
+#undef AUTHOR_
+ AUTHOR_DEFAULT = AUTHOR_FULL
+};
+
+static const struct enum_map author_map[] = {
+#define AUTHOR_(name) ENUM_MAP(#name, AUTHOR_##name)
+ AUTHOR_VALUES
+#undef AUTHOR_
+};
+
+/* FIXME: Handle multi-byte and multi-column characters. */
+static const char *
+get_author_initials(const char *author, size_t max_columns)
+{
+ static char initials[AUTHOR_COLS];
+ size_t pos;
+
+#define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
+
+ memset(initials, 0, sizeof(initials));
+ for (pos = 0; *author && pos < sizeof(initials) - 1; author++, pos++) {
+ while (is_initial_sep(*author))
+ author++;
+ strncpy(&initials[pos], author, sizeof(initials) - 1 - pos);
+ while (*author && author[1] && !is_initial_sep(author[1]))
+ author++;
+ }
+
+ return initials;
+}
+
+
static bool
argv_from_string(const char *argv[SIZEOF_ARG], int *argc, char *cmd)
{
int i;
for (i = 0; i < ARRAY_SIZE(req_info); i++)
- if (req_info[i].namelen == namelen &&
- !string_enum_compare(req_info[i].name, name, namelen))
+ if (enum_equals(req_info[i], name, namelen))
return req_info[i].request;
return REQ_NONE;
/* Option and state variables. */
static enum date opt_date = DATE_DEFAULT;
-static bool opt_author = TRUE;
+static enum author opt_author = AUTHOR_DEFAULT;
static bool opt_line_number = FALSE;
static bool opt_line_graphics = TRUE;
static bool opt_rev_graph = FALSE;
static double opt_hscroll = 0.50;
static double opt_scale_split_view = 2.0 / 3.0;
static int opt_tab_size = 8;
-static int opt_author_cols = 19;
+static int opt_author_cols = AUTHOR_COLS;
static char opt_path[SIZEOF_STR] = "";
static char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
enum line_type type;
for (type = 0; type < ARRAY_SIZE(line_info); type++)
- if (namelen == line_info[type].namelen &&
- !string_enum_compare(line_info[type].name, name, namelen))
+ if (enum_equals(line_info[type], name, namelen))
return &line_info[type];
return NULL;
return OK;
}
+static int parse_enum_do(unsigned int *opt, const char *arg,
+ const struct enum_map *map, size_t map_size)
+{
+ bool is_true;
+
+ assert(map_size > 1);
+
+ if (map_enum_do(map, map_size, (int *) opt, arg))
+ return OK;
+
+ if (parse_bool(&is_true, arg) != OK)
+ return ERR;
+
+ *opt = is_true ? map[1].value : map[0].value;
+ return OK;
+}
+
+#define parse_enum(opt, arg, map) \
+ parse_enum_do(opt, arg, map, ARRAY_SIZE(map))
+
static int
parse_string(char *opt, const char *arg, size_t optsize)
{
}
if (!strcmp(argv[0], "show-author"))
- return parse_bool(&opt_author, argv[2]);
-
- if (!strcmp(argv[0], "show-date")) {
- bool show_date;
-
- if (!strcmp(argv[2], "relative")) {
- opt_date = DATE_RELATIVE;
- return OK;
- } else if (!strcmp(argv[2], "short")) {
- opt_date = DATE_SHORT;
- return OK;
- } else if (parse_bool(&show_date, argv[2]) == OK) {
- opt_date = show_date ? DATE_DEFAULT : DATE_NONE;
- return OK;
- }
- return ERR;
- }
+ return parse_enum(&opt_author, argv[2], author_map);
+
+ if (!strcmp(argv[0], "show-date"))
+ return parse_enum(&opt_date, argv[2], date_map);
if (!strcmp(argv[0], "show-rev-graph"))
return parse_bool(&opt_rev_graph, argv[2]);
static bool
draw_author(struct view *view, const char *author)
{
- bool trim = opt_author_cols == 0 || opt_author_cols > 5 || !author;
-
- if (!trim) {
- static char initials[10];
- size_t pos;
-
-#define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@')
-
- memset(initials, 0, sizeof(initials));
- for (pos = 0; *author && pos < opt_author_cols - 1; author++, pos++) {
- while (is_initial_sep(*author))
- author++;
- strncpy(&initials[pos], author, sizeof(initials) - 1 - pos);
- while (*author && !is_initial_sep(author[1]))
- author++;
- }
+ bool trim = opt_author_cols == 0 || opt_author_cols > 5;
+ bool abbreviate = opt_author == AUTHOR_ABBREVIATED || !trim;
- author = initials;
- }
+ if (abbreviate && author)
+ author = get_author_initials(author, opt_author_cols);
return draw_field(view, LINE_AUTHOR, author, opt_author_cols, trim);
}
}
static void
-toggle_date_option(enum date *date)
+toggle_enum_option_do(unsigned int *opt, const char *help,
+ const struct enum_map *map, size_t size)
{
- static const char *help[] = {
- "no",
- "default",
- "relative",
- "short"
- };
-
- *date = (*date + 1) % ARRAY_SIZE(help);
+ *opt = (*opt + 1) % size;
redraw_display(FALSE);
- report("Displaying %s dates", help[*date]);
+ report("Displaying %s %s", enum_name(map[*opt]), help);
}
+#define toggle_enum_option(opt, help, map) \
+ toggle_enum_option_do(opt, help, map, ARRAY_SIZE(map))
+
+#define toggle_date() toggle_enum_option(&opt_date, "dates", date_map)
+#define toggle_author() toggle_enum_option(&opt_author, "author names", author_map)
+
static void
toggle_view_option(bool *option, const char *help)
{
if (prompt_menu("Toggle option", menu, &selected)) {
if (menu[selected].data == &opt_date)
- toggle_date_option(menu[selected].data);
+ toggle_date();
+ else if (menu[selected].data == &opt_author)
+ toggle_author();
else
toggle_view_option(menu[selected].data, menu[selected].text);
}
free((void *) argv[argc]);
}
+static const char *
+format_arg(const char *name)
+{
+ static struct {
+ const char *name;
+ size_t namelen;
+ const char *value;
+ const char *value_if_empty;
+ } vars[] = {
+#define FORMAT_VAR(name, value, value_if_empty) \
+ { name, STRING_SIZE(name), value, value_if_empty }
+ FORMAT_VAR("%(directory)", opt_path, ""),
+ FORMAT_VAR("%(file)", opt_file, ""),
+ FORMAT_VAR("%(ref)", opt_ref, "HEAD"),
+ FORMAT_VAR("%(head)", ref_head, ""),
+ FORMAT_VAR("%(commit)", ref_commit, ""),
+ FORMAT_VAR("%(blob)", ref_blob, ""),
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vars); i++)
+ if (!strncmp(name, vars[i].name, vars[i].namelen))
+ return *vars[i].value ? vars[i].value : vars[i].value_if_empty;
+
+ return NULL;
+}
static bool
format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags)
{
@@ -2921,27 +2994,13 @@ format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags fl
len = strlen(arg);
value = "";
- } else if (!prefixcmp(next, "%(directory)")) {
- value = opt_path;
-
- } else if (!prefixcmp(next, "%(file)")) {
- value = opt_file;
-
- } else if (!prefixcmp(next, "%(ref)")) {
- value = *opt_ref ? opt_ref : "HEAD";
-
- } else if (!prefixcmp(next, "%(head)")) {
- value = ref_head;
-
- } else if (!prefixcmp(next, "%(commit)")) {
- value = ref_commit;
-
- } else if (!prefixcmp(next, "%(blob)")) {
- value = ref_blob;
-
} else {
- report("Unknown replacement: `%s`", next);
- return FALSE;
+ value = format_arg(next);
+
+ if (!value) {
+ report("Unknown replacement: `%s`", next);
+ return FALSE;
+ }
}
if (!string_format_from(buf, &bufpos, "%.*s%s", len, arg, value))
{
if (view->pipe)
end_update(view, TRUE);
- return io_open(&view->io, "%s", name);
+ return io_open(&view->io, "%s/%s", opt_cdup[0] ? opt_cdup : ".", name);
}
static bool
}
static void
-open_editor(bool from_root, const char *file)
+open_editor(const char *file)
{
const char *editor_argv[] = { "vi", file, NULL };
const char *editor;
editor = "vi";
editor_argv[0] = editor;
- open_external_viewer(editor_argv, from_root ? opt_cdup : NULL);
+ open_external_viewer(editor_argv, opt_cdup);
}
static void
break;
case REQ_TOGGLE_DATE:
- toggle_date_option(&opt_date);
+ toggle_date();
break;
case REQ_TOGGLE_AUTHOR:
- toggle_view_option(&opt_author, "author display");
+ toggle_author();
break;
case REQ_TOGGLE_REV_GRAPH:
if (add_title && help_open_keymap_title(view, keymap))
return;
- add_title = false;
+ add_title = FALSE;
if (group) {
add_line_text(view, group, LINE_HELP_GROUP);
else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
report("Failed to save blob data to file");
else
- open_editor(FALSE, file);
+ open_editor(file);
if (fd != -1)
unlink(file);
}
} else if (!is_head_commit(view->vid)) {
open_blob_editor();
} else {
- open_editor(TRUE, opt_file);
+ open_editor(opt_file);
}
return REQ_NONE;
};
static const char *status_list_other_argv[] = {
- "git", "ls-files", "-z", "--others", "--exclude-standard", NULL
+ "git", "ls-files", "-z", "--others", "--exclude-standard", opt_prefix, NULL
};
static const char *status_list_no_head_argv[] = {
return REQ_NONE;
}
- open_editor(status->status != '?', status->new.name);
+ open_editor(status->new.name);
break;
case REQ_VIEW_BLAME:
return REQ_NONE;
}
- open_editor(stage_status.status != '?', stage_status.new.name);
+ open_editor(stage_status.new.name);
break;
case REQ_REFRESH: