index 7a996dca08ca43e21b13428ebfdb3f4d0109cd42..0f9ff04cbfe37e7564ffcb88405a134300a23d14 100644 (file)
--- a/tig.c
+++ b/tig.c
#include <sys/stat.h>
#include <sys/select.h>
#include <unistd.h>
+#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#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)
@@ -102,16 +104,11 @@ static size_t utf8_length(const char **string, size_t col, int *width, size_t ma
/* The format and size of the date column in the main view. */
#define DATE_FORMAT "%Y-%m-%d %H:%M"
#define DATE_COLS STRING_SIZE("2006-04-29 14:21 ")
+#define DATE_SHORT_COLS STRING_SIZE("2006-04-29 ")
-#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 char *prompt_input(const char *prompt, input_handler handler, void *data);
static bool prompt_yesno(const char *prompt);
+struct menu_item {
+ int hotkey;
+ const char *text;
+ void *data;
+};
+
+static bool prompt_menu(const char *prompt, const struct menu_item *items, int *selected);
+
/*
* Allocation helpers ... Entering macro hell to never be seen again.
*/
}
-static const char *
-mkdate(const time_t *time)
+/*
+ * What value of "tz" was in effect back then at "time" in the
+ * local timezone?
+ */
+static int local_tzoffset(time_t time)
+{
+ time_t t, t_local;
+ struct tm tm;
+ int offset, eastwest;
+
+ t = time;
+ localtime_r(&t, &tm);
+ t_local = mktime(&tm);
+
+ if (t_local < t) {
+ eastwest = -1;
+ offset = t - t_local;
+ } else {
+ eastwest = 1;
+ offset = t_local - t;
+ }
+ offset /= 60; /* in minutes */
+ offset = (offset % 60) + ((offset / 60) * 100);
+ return offset * eastwest;
+}
+
+enum date {
+ DATE_NONE = 0,
+ DATE_DEFAULT,
+ DATE_RELATIVE,
+ DATE_SHORT
+};
+
+static char *
+string_date(const time_t *time, enum date date)
{
static char buf[DATE_COLS + 1];
+ static const struct enum_map reldate[] = {
+ { "second", 1, 60 * 2 },
+ { "minute", 60, 60 * 60 * 2 },
+ { "hour", 60 * 60, 60 * 60 * 24 * 2 },
+ { "day", 60 * 60 * 24, 60 * 60 * 24 * 7 * 2 },
+ { "week", 60 * 60 * 24 * 7, 60 * 60 * 24 * 7 * 5 },
+ { "month", 60 * 60 * 24 * 30, 60 * 60 * 24 * 30 * 12 },
+ };
struct tm tm;
+ if (date == DATE_RELATIVE) {
+ struct timeval now;
+ time_t date = *time + local_tzoffset(*time);
+ time_t seconds;
+ int i;
+
+ gettimeofday(&now, NULL);
+ seconds = now.tv_sec < date ? date - now.tv_sec : now.tv_sec - date;
+ for (i = 0; i < ARRAY_SIZE(reldate); i++) {
+ if (seconds >= reldate[i].value)
+ continue;
+
+ seconds /= reldate[i].namelen;
+ if (!string_format(buf, "%ld %s%s %s",
+ seconds, reldate[i].name,
+ seconds > 1 ? "s" : "",
+ now.tv_sec >= date ? "ago" : "ahead"))
+ break;
+ return buf;
+ }
+ }
+
gmtime_r(time, &tm);
return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
}
return init_io_rd(io, argv, NULL, flags) && start_io(io);
}
+static bool
+run_io_rd_dir(struct io *io, const char **argv, const char *dir, enum format_flags flags)
+{
+ return init_io_rd(io, argv, dir, flags) && start_io(io);
+}
+
static bool
io_eof(struct io *io)
{
REQ_(FIND_PREV, "Find previous search match"), \
\
REQ_GROUP("Option manipulation") \
+ REQ_(OPTIONS, "Open option menu"), \
REQ_(TOGGLE_LINENO, "Toggle line numbers"), \
REQ_(TOGGLE_DATE, "Toggle date display"), \
+ REQ_(TOGGLE_DATE_SHORT, "Toggle short (date-only) dates"), \
REQ_(TOGGLE_AUTHOR, "Toggle author display"), \
REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"), \
REQ_(TOGGLE_REFS, "Toggle reference display (tags/branches)"), \
*/
/* Option and state variables. */
-static bool opt_date = TRUE;
+static enum date opt_date = DATE_DEFAULT;
static bool opt_author = TRUE;
static bool opt_line_number = FALSE;
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] = "";
#define is_initial_commit() (!*opt_head_rev)
#define is_head_commit(rev) (!strcmp((rev), "HEAD") || !strcmp(opt_head_rev, (rev)))
+#define mkdate(time) string_date(time, opt_date)
/*
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 */
};
{ 'z', REQ_STOP_LOADING },
{ 'v', REQ_SHOW_VERSION },
{ 'r', REQ_SCREEN_REDRAW },
+ { 'o', REQ_OPTIONS },
{ '.', REQ_TOGGLE_LINENO },
{ 'D', REQ_TOGGLE_DATE },
{ 'A', REQ_TOGGLE_AUTHOR },
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], "show-author"))
return parse_bool(&opt_author, argv[2]);
- if (!strcmp(argv[0], "show-date"))
- return parse_bool(&opt_date, 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])) {
+ opt_date = show_date ? DATE_DEFAULT : DATE_NONE;
+ }
+ return ERR;
+ }
if (!strcmp(argv[0], "show-rev-graph"))
return parse_bool(&opt_rev_graph, argv[2]);
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) {
bool (*grep)(struct view *view, struct line *line);
/* Select line */
void (*select)(struct view *view, struct line *line);
+ /* Prepare view for loading */
+ bool (*prepare)(struct view *view);
};
static struct view_ops blame_ops;
@@ -2010,9 +2157,10 @@ draw_field(struct view *view, enum line_type type, const char *text, int len, bo
static bool
draw_date(struct view *view, time_t *time)
{
- const char *date = mkdate(time);
+ const char *date = time ? mkdate(time) : "";
+ int cols = opt_date == DATE_SHORT ? DATE_SHORT_COLS : DATE_COLS;
- return draw_field(view, LINE_DATE, date, DATE_COLS, FALSE);
+ return draw_field(view, LINE_DATE, date, cols, FALSE);
}
static bool
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. */
}
}
+static void
+toggle_date_option(enum date *date)
+{
+ static const char *help[] = {
+ "no",
+ "default",
+ "relative",
+ "short"
+ };
+
+ opt_date = (opt_date + 1) % ARRAY_SIZE(help);
+ redraw_display(FALSE);
+ report("Displaying %s dates", help[opt_date]);
+}
+
static void
toggle_view_option(bool *option, const char *help)
{
report("%sabling %s", *option ? "En" : "Dis", help);
}
+static void
+open_option_menu(void)
+{
+ const struct menu_item menu[] = {
+ { '.', "line numbers", &opt_line_number },
+ { 'D', "date display", &opt_date },
+ { 'A', "author display", &opt_author },
+ { 'g', "revision graph display", &opt_rev_graph },
+ { 'F', "reference display", &opt_show_refs },
+ { 0 }
+ };
+ int selected = 0;
+
+ if (prompt_menu("Toggle option", menu, &selected)) {
+ if (menu[selected].data == &opt_date)
+ toggle_date_option(menu[selected].data);
+ else
+ toggle_view_option(menu[selected].data, menu[selected].text);
+ }
+}
+
static void
maximize_view(struct view *view)
{
@@ -2326,15 +2521,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)
return FALSE;
} else {
- if (view == VIEW(REQ_VIEW_TREE) && strcmp(view->vid, view->id))
- opt_path[0] = 0;
-
- if (!run_io_rd(&view->io, view->ops->argv, FORMAT_ALL))
+ if (view->ops->prepare) {
+ if (!view->ops->prepare(view))
+ return FALSE;
+ } else if (!run_io_rd(&view->io, view->ops->argv, FORMAT_ALL)) {
return FALSE;
+ }
/* Put the current ref_* value to the view title ref
* member. This is needed by the blob view. Most other
display[current_view] = view;
}
+ /* No parent signals that this is the first loaded view. */
+ if (prev && view != prev) {
+ view->parent = prev;
+ }
+
/* Resize the view when switching between split- and full-screen,
* or when switching between two different full-screen views. */
if (nviews != displayed_views() ||
/* "Blur" the previous view. */
update_view_title(prev);
}
-
- view->parent = prev;
}
if (view->pipe && view->lines == 0) {
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);
maximize_view(view);
break;
+ case REQ_OPTIONS:
+ open_option_menu();
+ break;
+
case REQ_TOGGLE_LINENO:
toggle_view_option(&opt_line_number, "line numbers");
break;
case REQ_TOGGLE_DATE:
- toggle_view_option(&opt_date, "date display");
+ toggle_date_option(&opt_date);
break;
case REQ_TOGGLE_AUTHOR:
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
@@ -3509,12 +3721,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,
};
return TRUE;
}
- if (!run_io_rd(&io, log_file, FORMAT_NONE)) {
+ if (!run_io_rd_dir(&io, log_file, opt_cdup, FORMAT_NONE)) {
report("Failed to load tree data");
return TRUE;
}
if (!pos)
return TRUE;
text = pos + 1;
- if (*opt_prefix && !strncmp(text, opt_prefix, strlen(opt_prefix)))
- text += strlen(opt_prefix);
if (*opt_path && !strncmp(text, opt_path, strlen(opt_path)))
text += strlen(opt_path);
pos = strchr(text, '/');
string_copy_rev(view->ref, entry->id);
}
+static bool
+tree_prepare(struct view *view)
+{
+ if (view->lines == 0 && opt_prefix[0]) {
+ char *pos = opt_prefix;
+
+ while (pos && *pos) {
+ char *end = strchr(pos, '/');
+
+ if (end)
+ *end = 0;
+ push_tree_stack_entry(pos, 0);
+ pos = end;
+ if (end) {
+ *end = '/';
+ pos++;
+ }
+ }
+
+ } else if (strcmp(view->vid, view->id)) {
+ opt_path[0] = 0;
+ }
+
+ return run_io_rd_dir(&view->io, view->ops->argv, opt_cdup, FORMAT_ALL);
+}
+
static const char *tree_argv[SIZEOF_ARG] = {
"git", "ls-tree", "%(commit)", "%(directory)", NULL
};
tree_request,
tree_grep,
tree_select,
+ tree_prepare,
};
static bool
static bool
blame_open(struct view *view)
{
- if (*opt_ref || !io_open(&view->io, opt_file)) {
- if (!run_io_rd(&view->io, blame_cat_file_argv, FORMAT_ALL))
+ char path[SIZEOF_STR];
+
+ if (!view->parent && *opt_prefix) {
+ string_copy(path, opt_file);
+ if (!string_format(opt_file, "%s%s", opt_prefix, path))
+ return FALSE;
+ }
+
+ if (!string_format(path, "%s%s", opt_cdup, opt_file))
+ return FALSE;
+
+ if (*opt_ref || !io_open(&view->io, path)) {
+ if (!run_io_rd_dir(&view->io, blame_cat_file_argv, opt_cdup, FORMAT_ALL))
return FALSE;
}
if (view->lines == 0 && !view->parent)
die("No blame exist for %s", view->vid);
- if (view->lines == 0 || !run_io_rd(&io, argv, FORMAT_ALL)) {
+ if (view->lines == 0 || !run_io_rd_dir(&io, argv, opt_cdup, FORMAT_ALL)) {
report("Failed to load blame data");
return TRUE;
}
setup_update(view, view->id);
foreach_ref(branch_open_visitor, view);
+ view->p_restore = TRUE;
return TRUE;
}
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);
return prompt_input(prompt, read_prompt_handler, NULL);
}
+static bool prompt_menu(const char *prompt, const struct menu_item *items, int *selected)
+{
+ enum input_status status = INPUT_OK;
+ int size = 0;
+
+ while (items[size].text)
+ size++;
+
+ while (status == INPUT_OK) {
+ const struct menu_item *item = &items[*selected];
+ int key;
+ int i;
+
+ mvwprintw(status_win, 0, 0, "%s (%d of %d) ",
+ prompt, *selected + 1, size);
+ if (item->hotkey)
+ wprintw(status_win, "[%c] ", (char) item->hotkey);
+ wprintw(status_win, "%s", item->text);
+ wclrtoeol(status_win);
+
+ key = get_input(COLS - 1);
+ switch (key) {
+ case KEY_RETURN:
+ case KEY_ENTER:
+ case '\n':
+ status = INPUT_STOP;
+ break;
+
+ case KEY_LEFT:
+ case KEY_UP:
+ *selected = *selected - 1;
+ if (*selected < 0)
+ *selected = size - 1;
+ break;
+
+ case KEY_RIGHT:
+ case KEY_DOWN:
+ *selected = (*selected + 1) % size;
+ break;
+
+ case KEY_ESC:
+ status = INPUT_CANCEL;
+ break;
+
+ default:
+ for (i = 0; items[i].text; i++)
+ if (items[i].hotkey == key) {
+ *selected = i;
+ status = INPUT_STOP;
+ break;
+ }
+ }
+ }
+
+ /* Clear the status window */
+ status_empty = FALSE;
+ report("");
+
+ return status != INPUT_CANCEL;
+}
+
/*
* Repository properties
*/
die("command too long");
}
- if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))
- die("Failed to format arguments");
+ if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))
+ die("Failed to format arguments");
return request;
}