index 9e4d7938adc51e73c4e2f56150c29e4bc11750ce..68465192d31d87bab36ca44ed434abeccaf74a86 100644 (file)
--- a/tig.c
+++ b/tig.c
/* ncurses(3): Must be defined to have extended wide-character functions. */
#define _XOPEN_SOURCE_EXTENDED
-#include <curses.h>
+#ifdef HAVE_NCURSESW_NCURSES_H
+#include <ncursesw/ncurses.h>
+#else
+#ifdef HAVE_NCURSES_NCURSES_H
+#include <ncurses/ncurses.h>
+#else
+#include <ncurses.h>
+#endif
+#endif
#if __GNUC__ >= 3
#define __NORETURN __attribute__((__noreturn__))
#endif
#define TIG_LS_REMOTE \
- "git ls-remote $(git rev-parse --git-dir) 2>/dev/null"
+ "git ls-remote . 2>/dev/null"
#define TIG_DIFF_CMD \
"git show --pretty=fuller --no-color --root --patch-with-stat --find-copies-harder -C %s 2>/dev/null"
REQ_(TOGGLE_REFS, "Toggle reference display (tags/branches)"), \
REQ_(STATUS_UPDATE, "Update file status"), \
REQ_(STATUS_MERGE, "Merge file using external tool"), \
+ REQ_(STAGE_NEXT, "Find next chunk to stage"), \
REQ_(TREE_PARENT, "Switch to parent directory in tree view"), \
REQ_(EDIT, "Open in editor"), \
REQ_(NONE, "Do nothing")
static bool opt_show_refs = TRUE;
static int opt_num_interval = NUMBER_INTERVAL;
static int opt_tab_size = TAB_SIZE;
-static enum request opt_request = REQ_VIEW_MAIN;
+static int opt_author_cols = AUTHOR_COLS-1;
static char opt_cmd[SIZEOF_STR] = "";
static char opt_path[SIZEOF_STR] = "";
static char opt_file[SIZEOF_STR] = "";
static signed char opt_is_inside_work_tree = -1; /* set to TRUE or FALSE */
static char opt_editor[SIZEOF_STR] = "";
-static bool
+static enum request
parse_options(int argc, char *argv[])
{
+ enum request request = REQ_VIEW_MAIN;
size_t buf_size;
char *subcommand;
bool seen_dashdash = FALSE;
int i;
if (!isatty(STDIN_FILENO)) {
- opt_request = REQ_VIEW_PAGER;
opt_pipe = stdin;
- return TRUE;
+ return REQ_VIEW_PAGER;
}
if (argc <= 1)
- return TRUE;
+ return REQ_VIEW_MAIN;
subcommand = argv[1];
if (!strcmp(subcommand, "status") || !strcmp(subcommand, "-S")) {
- opt_request = REQ_VIEW_STATUS;
if (!strcmp(subcommand, "-S"))
warn("`-S' has been deprecated; use `tig status' instead");
if (argc > 2)
warn("ignoring arguments after `%s'", subcommand);
- return TRUE;
+ return REQ_VIEW_STATUS;
} else if (!strcmp(subcommand, "blame")) {
- opt_request = REQ_VIEW_BLAME;
if (argc <= 2 || argc > 4)
die("invalid number of options to blame\n\n%s", usage);
}
string_ncopy(opt_file, argv[i], strlen(argv[i]));
- return TRUE;
+ return REQ_VIEW_BLAME;
} else if (!strcmp(subcommand, "show")) {
- opt_request = REQ_VIEW_DIFF;
+ request = REQ_VIEW_DIFF;
} else if (!strcmp(subcommand, "log") || !strcmp(subcommand, "diff")) {
- opt_request = subcommand[0] == 'l'
- ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
+ request = subcommand[0] == 'l' ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
warn("`tig %s' has been deprecated", subcommand);
} else {
} else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) {
printf("tig version %s\n", TIG_VERSION);
- return FALSE;
+ return REQ_NONE;
} else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) {
printf("%s\n", usage);
- return FALSE;
+ return REQ_NONE;
}
opt_cmd[buf_size++] = ' ';
opt_cmd[buf_size] = 0;
- return TRUE;
+ return request;
}
{ ':', REQ_PROMPT },
{ 'u', REQ_STATUS_UPDATE },
{ 'M', REQ_STATUS_MERGE },
+ { '@', REQ_STAGE_NEXT },
{ ',', REQ_TREE_PARENT },
{ 'e', REQ_EDIT },
static enum request
add_run_request(enum keymap keymap, int key, int argc, char **argv)
{
- struct run_request *tmp;
- struct run_request req = { keymap, key };
+ struct run_request *req;
+ char cmd[SIZEOF_STR];
size_t bufpos;
for (bufpos = 0; argc > 0; argc--, argv++)
- if (!string_format_from(req.cmd, &bufpos, "%s ", *argv))
+ if (!string_format_from(cmd, &bufpos, "%s ", *argv))
return REQ_NONE;
- req.cmd[bufpos - 1] = 0;
-
- tmp = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
- if (!tmp)
+ req = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
+ if (!req)
return REQ_NONE;
- run_request = tmp;
- run_request[run_requests++] = req;
+ run_request = req;
+ req = &run_request[run_requests++];
+ string_copy(req->cmd, cmd);
+ req->keymap = keymap;
+ req->key = key;
return REQ_NONE + run_requests;
}
!strcmp(s, "yes")) ? TRUE : FALSE;
}
+static int
+parse_int(const char *s, int default_value, int min, int max)
+{
+ int value = atoi(s);
+
+ return (value < min || value > max) ? default_value : value;
+}
+
/* Wants: name = value */
static int
option_set_command(int argc, char *argv[])
}
if (!strcmp(argv[0], "line-number-interval")) {
- opt_num_interval = atoi(argv[2]);
+ opt_num_interval = parse_int(argv[2], opt_num_interval, 1, 1024);
+ return OK;
+ }
+
+ if (!strcmp(argv[0], "author-width")) {
+ opt_author_cols = parse_int(argv[2], opt_author_cols, 0, 1024);
return OK;
}
if (!strcmp(argv[0], "tab-size")) {
- opt_tab_size = atoi(argv[2]);
+ opt_tab_size = parse_int(argv[2], opt_tab_size, 1, 1024);
return OK;
}
enum line_graphic {
- LINE_GRAPHIC_VLINE,
+ LINE_GRAPHIC_VLINE
};
static int line_graphics[] = {
*/
static void
-end_update(struct view *view)
+end_update(struct view *view, bool force)
{
if (!view->pipe)
return;
+ while (!view->ops->read(view, NULL))
+ if (!force)
+ return;
set_nonblocking_input(FALSE);
if (view->pipe == stdin)
fclose(view->pipe);
static bool
begin_update(struct view *view)
{
- if (view->pipe)
- end_update(view);
-
if (opt_cmd[0]) {
string_copy(view->cmd, opt_cmd);
opt_cmd[0] = 0;
check_pipe:
if (ferror(view->pipe)) {
report("Failed to read: %s", strerror(errno));
- goto end;
+ end_update(view, TRUE);
} else if (feof(view->pipe)) {
report("");
- goto end;
+ end_update(view, FALSE);
}
return TRUE;
alloc_error:
report("Allocation failure");
-
-end:
- if (view->ops->read(view, NULL))
- end_update(view);
+ end_update(view, TRUE);
return FALSE;
}
OPEN_SPLIT = 1, /* Split current view. */
OPEN_BACKGROUNDED = 2, /* Backgrounded. */
OPEN_RELOAD = 4, /* Reload view even if it is the current. */
- OPEN_NOMAXIMIZE = 8, /* Do not maximize the current view. */
+ OPEN_NOMAXIMIZE = 8 /* Do not maximize the current view. */
};
static void
(nviews == 1 && base_view != display[0]))
resize_display();
+ if (view->pipe)
+ end_update(view, TRUE);
+
if (view->ops->open) {
if (!view->ops->open(view)) {
report("Failed to load %s view", view->name);
redraw_display();
break;
- case REQ_PROMPT:
- /* Always reload^Wrerun commands from the prompt. */
- open_view(view, opt_request, OPEN_RELOAD);
- break;
-
case REQ_SEARCH:
case REQ_SEARCH_BACK:
search_view(view, request);
view = &views[i];
if (view->pipe)
report("Stopped loading the %s view", view->name),
- end_update(view);
+ end_update(view, TRUE);
}
break;
report("Nothing to edit");
break;
-
case REQ_ENTER:
report("Nothing to enter");
break;
-
case REQ_VIEW_CLOSE:
/* XXX: Mark closed views by letting view->parent point to the
* view itself. Parents to closed view should never be
size_t linelen = strlen(line);
struct blame *blame = malloc(sizeof(*blame) + linelen);
- if (!line)
- return FALSE;
-
blame->commit = NULL;
strncpy(blame->text, line, linelen);
blame->text[linelen] = 0;
return TRUE;
if (opt_author &&
- draw_field(view, LINE_MAIN_AUTHOR, author, AUTHOR_COLS, TRUE))
+ draw_field(view, LINE_MAIN_AUTHOR, author, opt_author_cols, TRUE))
return TRUE;
if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
static char status_onbranch[SIZEOF_STR];
static struct status stage_status;
static enum line_type stage_line_type;
+static size_t stage_chunks;
+static int *stage_chunk;
/* Get fields from the diff line:
* :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
return FALSE;
}
} else {
- char buf[] = { status->status, ' ', ' ', ' ', 0 };
+ static char buf[] = { '?', ' ', ' ', ' ', 0 };
+ buf[0] = status->status;
if (draw_text(view, line->type, buf, TRUE))
return TRUE;
type = LINE_DEFAULT;
}
stage_line_type = line->type;
+ stage_chunks = 0;
string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
}
return TRUE;
}
+static void
+stage_next(struct view *view, struct line *line)
+{
+ int i;
+
+ if (!stage_chunks) {
+ static size_t alloc = 0;
+ int *tmp;
+
+ for (line = view->line; line < view->line + view->lines; line++) {
+ if (line->type != LINE_DIFF_CHUNK)
+ continue;
+
+ tmp = realloc_items(stage_chunk, &alloc,
+ stage_chunks, sizeof(*tmp));
+ if (!tmp) {
+ report("Allocation failure");
+ return;
+ }
+
+ stage_chunk = tmp;
+ stage_chunk[stage_chunks++] = line - view->line;
+ }
+ }
+
+ for (i = 0; i < stage_chunks; i++) {
+ if (stage_chunk[i] > view->lineno) {
+ do_scroll_view(view, stage_chunk[i] - view->lineno);
+ report("Chunk %d of %d", i + 1, stage_chunks);
+ return;
+ }
+ }
+
+ report("No next chunk found");
+}
+
static enum request
stage_request(struct view *view, enum request request, struct line *line)
{
return REQ_NONE;
break;
+ case REQ_STAGE_NEXT:
+ if (stage_line_type == LINE_STAT_UNTRACKED) {
+ report("File is untracked; press %s to add",
+ get_key(REQ_STATUS_UPDATE));
+ return REQ_NONE;
+ }
+ stage_next(view, line);
+ return REQ_NONE;
+
case REQ_EDIT:
if (!stage_status.new.name[0])
return request;
return TRUE;
if (opt_author &&
- draw_field(view, LINE_MAIN_AUTHOR, commit->author, AUTHOR_COLS, TRUE))
+ draw_field(view, LINE_MAIN_AUTHOR, commit->author, opt_author_cols, TRUE))
return TRUE;
if (opt_rev_graph && commit->graph_size &&
if (load_git_config() == ERR)
die("Failed to load repo config.");
- if (!parse_options(argc, argv))
+ request = parse_options(argc, argv);
+ if (request == REQ_NONE)
return 0;
/* Require a git repository unless when running in pager mode. */
- if (!opt_git_dir[0] && opt_request != REQ_VIEW_PAGER)
+ if (!opt_git_dir[0] && request != REQ_VIEW_PAGER)
die("Not a git repository");
if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
view->cmd_env = getenv(view->cmd_env);
- request = opt_request;
-
init_display();
while (view_driver(display[current_view], request)) {
if (cmd && string_format(opt_cmd, "git %s", cmd)) {
if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
- opt_request = REQ_VIEW_DIFF;
+ request = REQ_VIEW_DIFF;
} else {
- opt_request = REQ_VIEW_PAGER;
+ request = REQ_VIEW_PAGER;
}
- break;
+
+ /* Always reload^Wrerun commands from the prompt. */
+ open_view(view, request, OPEN_RELOAD);
}
request = REQ_NONE;