index 87fc98656c6e95e1dbbecb31a031fcd0279ce093..47b0d4faa278cf507dd697893db12128236aae7d 100644 (file)
--- a/tig.c
+++ b/tig.c
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
#define STRING_SIZE(x) (sizeof(x) - 1)
@@ -103,15 +104,9 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
#define DATE_FORMAT "%Y-%m-%d %H:%M"
#define DATE_COLS STRING_SIZE("2006-04-29 14:21 ")
-#define AUTHOR_COLS 20
#define ID_COLS 8
-/* The default interval between line numbers. */
-#define NUMBER_INTERVAL 5
-
-#define TAB_SIZE 8
-
-#define SCALE_SPLIT_VIEW(height) ((height) * 2 / 3)
+#define MIN_VIEW_HEIGHT 4
#define NULL_ID "0000000000000000000000000000000000000000"
static bool opt_line_graphics = TRUE;
static bool opt_rev_graph = FALSE;
static bool opt_show_refs = TRUE;
-static int opt_num_interval = NUMBER_INTERVAL;
+static int opt_num_interval = 5;
static double opt_hscroll = 0.50;
-static int opt_tab_size = TAB_SIZE;
-static int opt_author_cols = AUTHOR_COLS-1;
+static double opt_scale_split_view = 2.0 / 3.0;
+static int opt_tab_size = 8;
+static int opt_author_cols = 19;
static char opt_path[SIZEOF_STR] = "";
static char opt_file[SIZEOF_STR] = "";
static char opt_ref[SIZEOF_REF] = "";
LINE(STAT_STAGED, "", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
LINE(STAT_UNSTAGED,"", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
LINE(STAT_UNTRACKED,"", COLOR_MAGENTA, COLOR_DEFAULT, 0), \
+LINE(HELP_KEYMAP, "", COLOR_CYAN, COLOR_DEFAULT, 0), \
+LINE(HELP_GROUP, "", COLOR_BLUE, COLOR_DEFAULT, 0), \
LINE(BLAME_ID, "", COLOR_MAGENTA, COLOR_DEFAULT, 0)
enum line_type {
unsigned int selected:1;
unsigned int dirty:1;
unsigned int cleareol:1;
+ unsigned int other:16;
void *data; /* User data */
};
return seq ? seq : "(no key)";
}
+static bool
+append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
+{
+ const char *sep = *pos > 0 ? ", " : "";
+ const char *keyname = get_key_name(keybinding->alias);
+
+ return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
+}
+
+static bool
+append_keymap_request_keys(char *buf, size_t *pos, enum request request,
+ enum keymap keymap, bool all)
+{
+ int i;
+
+ for (i = 0; i < keybindings[keymap].size; i++) {
+ if (keybindings[keymap].data[i].request == request) {
+ if (!append_key(buf, pos, &keybindings[keymap].data[i]))
+ return FALSE;
+ if (!all)
+ break;
+ }
+ }
+
+ return TRUE;
+}
+
+#define get_key(keymap, request) get_keys(keymap, request, FALSE)
+
static const char *
-get_key(enum request request)
+get_keys(enum keymap keymap, enum request request, bool all)
{
static char buf[BUFSIZ];
size_t pos = 0;
- char *sep = "";
int i;
buf[pos] = 0;
- for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
- const struct keybinding *keybinding = &default_keybindings[i];
+ if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
+ return "Too many keybindings!";
+ if (pos > 0 && !all)
+ return buf;
- if (keybinding->request != request)
- continue;
+ if (keymap != KEYMAP_GENERIC) {
+ /* Only the generic keymap includes the default keybindings when
+ * listing all keys. */
+ if (all)
+ return buf;
- if (!string_format_from(buf, &pos, "%s%s", sep,
- get_key_name(keybinding->alias)))
+ if (!append_keymap_request_keys(buf, &pos, request, KEYMAP_GENERIC, all))
return "Too many keybindings!";
- sep = ", ";
+ if (pos)
+ return buf;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
+ if (default_keybindings[i].request == request) {
+ if (!append_key(buf, &pos, &default_keybindings[i]))
+ return "Too many keybindings!";
+ if (!all)
+ return buf;
+ }
}
return buf;
{
struct line_info *info;
- if (argc != 3 && argc != 4) {
+ if (argc < 3) {
config_msg = "Wrong number of arguments given to color command";
return ERR;
}
return ERR;
}
- if (argc == 4 && !set_attribute(&info->attr, argv[3])) {
- config_msg = "Unknown attribute";
- return ERR;
+ info->attr = 0;
+ while (argc-- > 3) {
+ int attr;
+
+ if (!set_attribute(&attr, argv[argc])) {
+ config_msg = "Unknown attribute";
+ return ERR;
+ }
+ info->attr |= attr;
}
return OK;
if (!strcmp(argv[0], "horizontal-scroll"))
return parse_step(&opt_hscroll, argv[2]);
+ if (!strcmp(argv[0], "split-view-height"))
+ return parse_step(&opt_scale_split_view, argv[2]);
+
if (!strcmp(argv[0], "tab-size"))
return parse_int(&opt_tab_size, argv[2], 1, 1024);
option_bind_command(int argc, const char *argv[])
{
enum request request;
- int keymap;
+ int keymap = -1;
int key;
if (argc < 3) {
wnoutrefresh(view->title);
}
+static int
+apply_step(double step, int value)
+{
+ if (step >= 1)
+ return (int) step;
+ value *= step + 0.01;
+ return value ? value : 1;
+}
+
static void
resize_display(void)
{
if (view != base) {
/* Horizontal split. */
view->width = base->width;
- view->height = SCALE_SPLIT_VIEW(base->height);
+ view->height = apply_step(opt_scale_split_view, base->height);
+ view->height = MAX(view->height, MIN_VIEW_HEIGHT);
+ view->height = MIN(view->height, base->height - MIN_VIEW_HEIGHT);
base->height -= view->height;
/* Make room for the title bar. */
@@ -2353,15 +2414,6 @@ goto_view_line(struct view *view, unsigned long offset, unsigned long lineno)
return FALSE;
}
-static int
-apply_step(double step, int value)
-{
- if (step >= 1)
- return (int) step;
- value *= step + 0.01;
- return value ? value : 1;
-}
-
/* Scrolling backend */
static void
do_scroll_view(struct view *view, int lines)
case REQ_VIEW_BLAME:
if (!opt_file[0]) {
report("No file chosen, press %s to open tree view",
- get_key(REQ_VIEW_TREE));
+ get_key(view->keymap, REQ_VIEW_TREE));
break;
}
open_view(view, request, OPEN_DEFAULT);
case REQ_VIEW_BLOB:
if (!ref_blob[0]) {
report("No file chosen, press %s to open tree view",
- get_key(REQ_VIEW_TREE));
+ get_key(view->keymap, REQ_VIEW_TREE));
break;
}
open_view(view, request, OPEN_DEFAULT);
case REQ_VIEW_PAGER:
if (!VIEW(REQ_VIEW_PAGER)->pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
report("No pager content, press %s to run command from prompt",
- get_key(REQ_PROMPT));
+ get_key(view->keymap, REQ_PROMPT));
break;
}
open_view(view, request, OPEN_DEFAULT);
case REQ_VIEW_STAGE:
if (!VIEW(REQ_VIEW_STAGE)->lines) {
report("No stage content, press %s to open the status view and choose file",
- get_key(REQ_VIEW_STATUS));
+ get_key(view->keymap, REQ_VIEW_STATUS));
break;
}
open_view(view, request, OPEN_DEFAULT);
return FALSE;
default:
- report("Unknown key, press 'h' for help");
+ report("Unknown key, press %s for help",
+ get_key(view->keymap, REQ_VIEW_HELP));
return TRUE;
}
}
}
-static enum input_status
-select_commit_parent_handler(void *data, char *buf, int c)
+static bool
+open_commit_parent_menu(char buf[SIZEOF_STR], int *parents)
{
- size_t parents = *(size_t *) data;
- int parent = 0;
+ char rev[SIZEOF_REV];
+ const char *revlist_argv[] = {
+ "git", "log", "--no-color", "-1", "--pretty=format:%s", rev, NULL
+ };
+ struct menu_item *items;
+ char text[SIZEOF_STR];
+ bool ok = TRUE;
+ int i;
- if (!isdigit(c))
- return INPUT_SKIP;
+ items = calloc(*parents + 1, sizeof(*items));
+ if (!items)
+ return FALSE;
- if (*buf)
- parent = atoi(buf) * 10;
- parent += c - '0';
+ for (i = 0; i < *parents; i++) {
+ string_copy_rev(rev, &buf[SIZEOF_REV * i]);
+ if (!run_io_buf(revlist_argv, text, sizeof(text)) ||
+ !(items[i].text = strdup(text))) {
+ ok = FALSE;
+ break;
+ }
+ }
- if (parent > parents)
- return INPUT_SKIP;
- return INPUT_OK;
+ if (ok) {
+ *parents = 0;
+ ok = prompt_menu("Select parent", items, parents);
+ }
+ for (i = 0; items[i].text; i++)
+ free((char *) items[i].text);
+ free(items);
+ return ok;
}
static bool
@@ -3540,12 +3610,13 @@ select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path)
{
char buf[SIZEOF_STR * 4];
const char *revlist_argv[] = {
- "git", "rev-list", "-1", "--parents", id, "--", path, NULL
+ "git", "log", "--no-color", "-1",
+ "--pretty=format:%P", id, "--", path, NULL
};
int parents;
if (!run_io_buf(revlist_argv, buf, sizeof(buf)) ||
- (parents = (strlen(buf) / 40) - 1) < 0) {
+ (parents = strlen(buf) / 40) < 0) {
report("Failed to get parent information");
return FALSE;
return FALSE;
}
- if (parents > 1) {
- char prompt[SIZEOF_STR];
- char *result;
-
- if (!string_format(prompt, "Which parent? [1..%d] ", parents))
- return FALSE;
- result = prompt_input(prompt, select_commit_parent_handler, &parents);
- if (!result)
- return FALSE;
- parents = atoi(result);
- }
+ if (parents > 1 && !open_commit_parent_menu(buf, &parents))
+ return FALSE;
string_copy_rev(rev, &buf[41 * parents]);
return TRUE;
* Help backend
*/
+static bool help_keymap_hidden[ARRAY_SIZE(keymap_table)];
+
+static char *
+help_name(char buf[SIZEOF_STR], const char *name, size_t namelen)
+{
+ int bufpos;
+
+ for (bufpos = 0; bufpos <= namelen; bufpos++) {
+ buf[bufpos] = tolower(name[bufpos]);
+ if (buf[bufpos] == '_')
+ buf[bufpos] = '-';
+ }
+
+ buf[bufpos] = 0;
+ return buf;
+}
+
+#define help_keymap_name(buf, keymap) \
+ help_name(buf, keymap_table[keymap].name, keymap_table[keymap].namelen)
+
static bool
-help_open(struct view *view)
+help_open_keymap_title(struct view *view, enum keymap keymap)
{
char buf[SIZEOF_STR];
- size_t bufpos;
- int i;
+ struct line *line;
- if (view->lines > 0)
- return TRUE;
+ line = add_line_format(view, LINE_HELP_KEYMAP, "[%c] %s bindings",
+ help_keymap_hidden[keymap] ? '+' : '-',
+ help_keymap_name(buf, keymap));
+ if (line)
+ line->other = keymap;
- add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
+ return help_keymap_hidden[keymap];
+}
+
+static void
+help_open_keymap(struct view *view, enum keymap keymap)
+{
+ const char *group = NULL;
+ char buf[SIZEOF_STR];
+ size_t bufpos;
+ bool add_title = TRUE;
+ int i;
for (i = 0; i < ARRAY_SIZE(req_info); i++) {
- const char *key;
+ const char *key = NULL;
if (req_info[i].request == REQ_NONE)
continue;
if (!req_info[i].request) {
- add_line_text(view, "", LINE_DEFAULT);
- add_line_text(view, req_info[i].help, LINE_DEFAULT);
+ group = req_info[i].help;
continue;
}
- key = get_key(req_info[i].request);
- if (!*key)
- key = "(no key defined)";
+ key = get_keys(keymap, req_info[i].request, TRUE);
+ if (!key || !*key)
+ continue;
+
+ if (add_title && help_open_keymap_title(view, keymap))
+ return;
+ add_title = false;
- for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
- buf[bufpos] = tolower(req_info[i].name[bufpos]);
- if (buf[bufpos] == '_')
- buf[bufpos] = '-';
+ if (group) {
+ add_line_text(view, group, LINE_HELP_GROUP);
+ group = NULL;
}
- add_line_format(view, LINE_DEFAULT, " %-25s %-20s %s",
- key, buf, req_info[i].help);
+ add_line_format(view, LINE_DEFAULT, " %-25s %-20s %s", key,
+ help_name(buf, req_info[i].name, req_info[i].namelen),
+ req_info[i].help);
}
- if (run_requests) {
- add_line_text(view, "", LINE_DEFAULT);
- add_line_text(view, "External commands:", LINE_DEFAULT);
- }
+ group = "External commands:";
for (i = 0; i < run_requests; i++) {
struct run_request *req = get_run_request(REQ_NONE + i + 1);
const char *key;
int argc;
- if (!req)
+ if (!req || req->keymap != keymap)
continue;
key = get_key_name(req->key);
if (!*key)
key = "(no key defined)";
+ if (add_title && help_open_keymap_title(view, keymap))
+ return;
+ if (group) {
+ add_line_text(view, group, LINE_HELP_GROUP);
+ group = NULL;
+ }
+
for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
if (!string_format_from(buf, &bufpos, "%s%s",
argc ? " " : "", req->argv[argc]))
- return REQ_NONE;
+ return;
- add_line_format(view, LINE_DEFAULT, " %-10s %-14s `%s`",
- keymap_table[req->keymap].name, key, buf);
+ add_line_format(view, LINE_DEFAULT, " %-25s `%s`", key, buf);
}
+}
+
+static bool
+help_open(struct view *view)
+{
+ enum keymap keymap;
+
+ reset_view(view);
+ add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
+ add_line_text(view, "", LINE_DEFAULT);
+
+ for (keymap = 0; keymap < ARRAY_SIZE(keymap_table); keymap++)
+ help_open_keymap(view, keymap);
return TRUE;
}
+static enum request
+help_request(struct view *view, enum request request, struct line *line)
+{
+ switch (request) {
+ case REQ_ENTER:
+ if (line->type == LINE_HELP_KEYMAP) {
+ help_keymap_hidden[line->other] =
+ !help_keymap_hidden[line->other];
+ view->p_restore = TRUE;
+ open_view(view, REQ_VIEW_HELP, OPEN_REFRESH);
+ }
+
+ return REQ_NONE;
+ default:
+ return pager_request(view, request, line);
+ }
+}
+
static struct view_ops help_ops = {
"line",
NULL,
help_open,
NULL,
pager_draw,
- pager_request,
+ help_request,
pager_grep,
pager_select,
};
struct line *pos = view->line + view->lines;
int files = 0;
int file, done;
- int cursor_y, cursor_x;
+ int cursor_y = -1, cursor_x = -1;
if (!status_update_prepare(&io, line->type))
return FALSE;
if (status && status->status == 'U') {
text = "Press %s to resolve conflict in %s";
- key = get_key(REQ_STATUS_MERGE);
+ key = get_key(KEYMAP_STATUS, REQ_STATUS_MERGE);
} else {
- key = get_key(REQ_STATUS_UPDATE);
+ key = get_key(KEYMAP_STATUS, REQ_STATUS_UPDATE);
}
string_format(view->ref, text, key, file);
case REQ_STAGE_NEXT:
if (stage_line_type == LINE_STAT_UNTRACKED) {
report("File is untracked; press %s to add",
- get_key(REQ_STATUS_UPDATE));
+ get_key(KEYMAP_STAGE, REQ_STATUS_UPDATE));
return REQ_NONE;
}
stage_next(view, line);