index ef8f6caccb4faaaf6dd17430f2652b7e61cdc0f2..68465192d31d87bab36ca44ed434abeccaf74a86 100644 (file)
--- a/tig.c
+++ b/tig.c
/* ncurses(3): Must be defined to have extended wide-character functions. */
#define _XOPEN_SOURCE_EXTENDED
/* 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__))
#if __GNUC__ >= 3
#define __NORETURN __attribute__((__noreturn__))
static void report(const char *msg, ...);
static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
static void set_nonblocking_input(bool loading);
static void report(const char *msg, ...);
static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
static void set_nonblocking_input(bool loading);
-static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve);
+static size_t utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve);
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define MIN(x, y) ((x) < (y) ? (x) : (y))
@@ -109,7 +117,7 @@ static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bo
#endif
#define TIG_LS_REMOTE \
#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"
#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_(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")
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 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 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 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[])
{
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)) {
size_t buf_size;
char *subcommand;
bool seen_dashdash = FALSE;
int i;
if (!isatty(STDIN_FILENO)) {
- opt_request = REQ_VIEW_PAGER;
opt_pipe = stdin;
opt_pipe = stdin;
- return TRUE;
+ return REQ_VIEW_PAGER;
}
if (argc <= 1)
}
if (argc <= 1)
- return TRUE;
+ return REQ_VIEW_MAIN;
subcommand = argv[1];
if (!strcmp(subcommand, "status") || !strcmp(subcommand, "-S")) {
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);
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")) {
} 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);
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]));
}
string_ncopy(opt_file, argv[i], strlen(argv[i]));
- return TRUE;
+ return REQ_VIEW_BLAME;
} else if (!strcmp(subcommand, "show")) {
} else if (!strcmp(subcommand, "show")) {
- opt_request = REQ_VIEW_DIFF;
+ request = REQ_VIEW_DIFF;
} else if (!strcmp(subcommand, "log") || !strcmp(subcommand, "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 {
warn("`tig %s' has been deprecated", subcommand);
} else {
} else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) {
printf("tig version %s\n", TIG_VERSION);
} 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);
} 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++] = ' ';
opt_cmd[buf_size] = 0;
opt_cmd[buf_size] = 0;
- return TRUE;
+ return request;
}
}
enum line_type {
#define LINE(type, line, fg, bg, attr) \
LINE_##type
enum line_type {
#define LINE(type, line, fg, bg, attr) \
LINE_##type
- LINE_INFO
+ LINE_INFO,
+ LINE_NONE
#undef LINE
};
#undef LINE
};
{ ':', REQ_PROMPT },
{ 'u', REQ_STATUS_UPDATE },
{ 'M', REQ_STATUS_MERGE },
{ ':', REQ_PROMPT },
{ 'u', REQ_STATUS_UPDATE },
{ 'M', REQ_STATUS_MERGE },
+ { '@', REQ_STAGE_NEXT },
{ ',', REQ_TREE_PARENT },
{ 'e', REQ_EDIT },
{ ',', REQ_TREE_PARENT },
{ 'e', REQ_EDIT },
static enum request
add_run_request(enum keymap keymap, int key, int argc, char **argv)
{
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++)
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;
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;
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;
}
return REQ_NONE + run_requests;
}
!strcmp(s, "yes")) ? TRUE : FALSE;
}
!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[])
/* Wants: name = value */
static int
option_set_command(int argc, char *argv[])
}
if (!strcmp(argv[0], "line-number-interval")) {
}
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")) {
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;
}
return OK;
}
size_t line_size; /* Total number of used lines */
unsigned int digits; /* Number of digits in the lines member. */
size_t line_size; /* Total number of used lines */
unsigned int digits; /* Number of digits in the lines member. */
+ /* Drawing */
+ struct line *curline; /* Line currently being drawn. */
+ enum line_type curtype; /* Attribute currently used for drawing. */
+ unsigned long col; /* Column when drawing. */
+
/* Loading */
FILE *pipe;
time_t start_time;
/* Loading */
FILE *pipe;
time_t start_time;
/* Read one line; updates view->line. */
bool (*read)(struct view *view, char *data);
/* Draw one line; @lineno must be < view->height. */
/* Read one line; updates view->line. */
bool (*read)(struct view *view, char *data);
/* Draw one line; @lineno must be < view->height. */
- bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
+ bool (*draw)(struct view *view, struct line *line, unsigned int lineno);
/* Depending on view handle a special requests. */
enum request (*request)(struct view *view, enum request request, struct line *line);
/* Search for regex in a line. */
/* Depending on view handle a special requests. */
enum request (*request)(struct view *view, enum request request, struct line *line);
/* Search for regex in a line. */
enum line_graphic {
enum line_graphic {
- LINE_GRAPHIC_VLINE,
+ LINE_GRAPHIC_VLINE
};
static int line_graphics[] = {
/* LINE_GRAPHIC_VLINE: */ '|'
};
};
static int line_graphics[] = {
/* LINE_GRAPHIC_VLINE: */ '|'
};
+static inline void
+set_view_attr(struct view *view, enum line_type type)
+{
+ if (!view->curline->selected && view->curtype != type) {
+ wattrset(view->win, get_line_attr(type));
+ wchgat(view->win, -1, 0, type, NULL);
+ view->curtype = type;
+ }
+}
+
static int
static int
-draw_text(struct view *view, const char *string, int max_len,
- bool use_tilde, bool selected)
+draw_chars(struct view *view, enum line_type type, const char *string,
+ int max_len, bool use_tilde)
{
int len = 0;
{
int len = 0;
+ int col = 0;
int trimmed = FALSE;
if (max_len <= 0)
return 0;
if (opt_utf8) {
int trimmed = FALSE;
if (max_len <= 0)
return 0;
if (opt_utf8) {
- len = utf8_length(string, max_len, &trimmed, use_tilde);
+ len = utf8_length(string, &col, max_len, &trimmed, use_tilde);
} else {
} else {
- len = strlen(string);
+ col = len = strlen(string);
if (len > max_len) {
if (use_tilde) {
max_len -= 1;
}
if (len > max_len) {
if (use_tilde) {
max_len -= 1;
}
- len = max_len;
+ col = len = max_len;
trimmed = TRUE;
}
}
trimmed = TRUE;
}
}
+ set_view_attr(view, type);
waddnstr(view->win, string, len);
if (trimmed && use_tilde) {
waddnstr(view->win, string, len);
if (trimmed && use_tilde) {
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_DELIMITER));
+ set_view_attr(view, LINE_DELIMITER);
waddch(view->win, '~');
waddch(view->win, '~');
- len++;
+ col++;
}
}
- return len;
+ return col;
}
static int
}
static int
-draw_lineno(struct view *view, unsigned int lineno, int max, bool selected)
+draw_space(struct view *view, enum line_type type, int max, int spaces)
+{
+ static char space[] = " ";
+ int col = 0;
+
+ spaces = MIN(max, spaces);
+
+ while (spaces > 0) {
+ int len = MIN(spaces, sizeof(space) - 1);
+
+ col += draw_chars(view, type, space, spaces, FALSE);
+ spaces -= len;
+ }
+
+ return col;
+}
+
+static bool
+draw_lineno(struct view *view, unsigned int lineno)
{
{
- static char fmt[] = "%1ld";
- char number[10] = " ";
+ char number[10];
int digits3 = view->digits < 3 ? 3 : view->digits;
int max_number = MIN(digits3, STRING_SIZE(number));
int digits3 = view->digits < 3 ? 3 : view->digits;
int max_number = MIN(digits3, STRING_SIZE(number));
- bool showtrimmed = FALSE;
+ int max = view->width - view->col;
int col;
int col;
+ if (max < max_number)
+ max_number = max;
+
lineno += view->offset + 1;
if (lineno == 1 || (lineno % opt_num_interval) == 0) {
lineno += view->offset + 1;
if (lineno == 1 || (lineno % opt_num_interval) == 0) {
+ static char fmt[] = "%1ld";
+
if (view->digits <= 9)
fmt[1] = '0' + digits3;
if (!string_format(number, fmt, lineno))
number[0] = 0;
if (view->digits <= 9)
fmt[1] = '0' + digits3;
if (!string_format(number, fmt, lineno))
number[0] = 0;
- showtrimmed = TRUE;
+ col = draw_chars(view, LINE_LINE_NUMBER, number, max_number, TRUE);
+ } else {
+ col = draw_space(view, LINE_LINE_NUMBER, max_number, max_number);
}
}
- if (max < max_number)
- max_number = max;
-
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_LINE_NUMBER));
- col = draw_text(view, number, max_number, showtrimmed, selected);
if (col < max) {
if (col < max) {
- if (!selected)
- wattrset(view->win, A_NORMAL);
+ set_view_attr(view, LINE_DEFAULT);
waddch(view->win, line_graphics[LINE_GRAPHIC_VLINE]);
col++;
}
waddch(view->win, line_graphics[LINE_GRAPHIC_VLINE]);
col++;
}
- if (col < max) {
+
+ if (col < max)
+ col += draw_space(view, LINE_DEFAULT, max - col, 1);
+ view->col += col;
+
+ return view->width - view->col <= 0;
+}
+
+static bool
+draw_text(struct view *view, enum line_type type, const char *string, bool trim)
+{
+ view->col += draw_chars(view, type, string, view->width - view->col, trim);
+ return view->width - view->col <= 0;
+}
+
+static bool
+draw_graphic(struct view *view, enum line_type type, chtype graphic[], size_t size)
+{
+ int max = view->width - view->col;
+ int i;
+
+ if (max < size)
+ size = max;
+
+ set_view_attr(view, type);
+ /* Using waddch() instead of waddnstr() ensures that
+ * they'll be rendered correctly for the cursor line. */
+ for (i = 0; i < size; i++)
+ waddch(view->win, graphic[i]);
+
+ view->col += size;
+ if (size < max) {
waddch(view->win, ' ');
waddch(view->win, ' ');
- col++;
+ view->col++;
}
}
- return col;
+ return view->width - view->col <= 0;
}
}
-static int
-draw_date(struct view *view, struct tm *time, int max, bool selected)
+static bool
+draw_field(struct view *view, enum line_type type, char *text, int len, bool trim)
{
{
- char buf[DATE_COLS];
+ int max = MIN(view->width - view->col, len);
int col;
int col;
+
+ if (text)
+ col = draw_chars(view, type, text, max - 1, trim);
+ else
+ col = draw_space(view, type, max - 1, max - 1);
+
+ view->col += col + draw_space(view, LINE_DEFAULT, max - col, max - col);
+ return view->width - view->col <= 0;
+}
+
+static bool
+draw_date(struct view *view, struct tm *time)
+{
+ char buf[DATE_COLS];
+ char *date;
int timelen = 0;
int timelen = 0;
- if (max > DATE_COLS)
- max = DATE_COLS;
if (time)
timelen = strftime(buf, sizeof(buf), DATE_FORMAT, time);
if (time)
timelen = strftime(buf, sizeof(buf), DATE_FORMAT, time);
- if (!timelen) {
- memset(buf, ' ', sizeof(buf) - 1);
- buf[sizeof(buf) - 1] = 0;
- }
-
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_DATE));
- col = draw_text(view, buf, max, FALSE, selected);
- if (col < max) {
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_DEFAULT));
- waddch(view->win, ' ');
- col++;
- }
+ date = timelen ? buf : NULL;
- return col;
+ return draw_field(view, LINE_DATE, date, DATE_COLS, FALSE);
}
static bool
}
static bool
line = &view->line[view->offset + lineno];
wmove(view->win, lineno, 0);
line = &view->line[view->offset + lineno];
wmove(view->win, lineno, 0);
+ view->col = 0;
+ view->curline = line;
+ view->curtype = LINE_NONE;
+ line->selected = FALSE;
if (selected) {
if (selected) {
+ set_view_attr(view, LINE_CURSOR);
line->selected = TRUE;
view->ops->select(view, line);
line->selected = TRUE;
view->ops->select(view, line);
- wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
- wattrset(view->win, get_line_attr(LINE_CURSOR));
} else if (line->selected) {
} else if (line->selected) {
- line->selected = FALSE;
wclrtoeol(view->win);
}
scrollok(view->win, FALSE);
wclrtoeol(view->win);
}
scrollok(view->win, FALSE);
- draw_ok = view->ops->draw(view, line, lineno, selected);
+ draw_ok = view->ops->draw(view, line, lineno);
scrollok(view->win, TRUE);
return draw_ok;
scrollok(view->win, TRUE);
return draw_ok;
*/
static void
*/
static void
-end_update(struct view *view)
+end_update(struct view *view, bool force)
{
if (!view->pipe)
return;
{
if (!view->pipe)
return;
+ while (!view->ops->read(view, NULL))
+ if (!force)
+ return;
set_nonblocking_input(FALSE);
if (view->pipe == stdin)
fclose(view->pipe);
set_nonblocking_input(FALSE);
if (view->pipe == stdin)
fclose(view->pipe);
static bool
begin_update(struct view *view)
{
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;
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));
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("");
} else if (feof(view->pipe)) {
report("");
- goto end;
+ end_update(view, FALSE);
}
return TRUE;
alloc_error:
report("Allocation failure");
}
return TRUE;
alloc_error:
report("Allocation failure");
-
-end:
- if (view->ops->read(view, NULL))
- end_update(view);
+ end_update(view, TRUE);
return FALSE;
}
return FALSE;
}
OPEN_SPLIT = 1, /* Split current view. */
OPEN_BACKGROUNDED = 2, /* Backgrounded. */
OPEN_RELOAD = 4, /* Reload view even if it is the current. */
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
};
static void
(nviews == 1 && base_view != display[0]))
resize_display();
(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);
if (view->ops->open) {
if (!view->ops->open(view)) {
report("Failed to load %s view", view->name);
redraw_display();
break;
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);
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),
view = &views[i];
if (view->pipe)
report("Stopped loading the %s view", view->name),
- end_update(view);
+ end_update(view, TRUE);
}
break;
}
break;
report("Nothing to edit");
break;
report("Nothing to edit");
break;
-
case REQ_ENTER:
report("Nothing to enter");
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
case REQ_VIEW_CLOSE:
/* XXX: Mark closed views by letting view->parent point to the
* view itself. Parents to closed view should never be
*/
static bool
*/
static bool
-pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
+pager_draw(struct view *view, struct line *line, unsigned int lineno)
{
char *text = line->data;
{
char *text = line->data;
- int col = 0;
- if (opt_line_number) {
- col += draw_lineno(view, lineno, view->width, selected);
- if (col >= view->width)
- return TRUE;
- }
-
- if (!selected)
- wattrset(view->win, get_line_attr(line->type));
+ if (opt_line_number && draw_lineno(view, lineno))
+ return TRUE;
- draw_text(view, text, view->width - col, TRUE, selected);
+ draw_text(view, line->type, text, TRUE);
return TRUE;
}
return TRUE;
}
size_t linelen = strlen(line);
struct blame *blame = malloc(sizeof(*blame) + linelen);
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;
blame->commit = NULL;
strncpy(blame->text, line, linelen);
blame->text[linelen] = 0;
}
static bool
}
static bool
-blame_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
+blame_draw(struct view *view, struct line *line, unsigned int lineno)
{
struct blame *blame = line->data;
{
struct blame *blame = line->data;
- int col = 0;
-
- if (opt_date) {
- struct tm *time = blame->commit && *blame->commit->filename
- ? &blame->commit->time : NULL;
+ struct tm *time = NULL;
+ char *id = NULL, *author = NULL;
- col += draw_date(view, time, view->width, selected);
- if (col >= view->width)
- return TRUE;
+ if (blame->commit && *blame->commit->filename) {
+ id = blame->commit->id;
+ author = blame->commit->author;
+ time = &blame->commit->time;
}
}
- if (opt_author) {
- int max = MIN(AUTHOR_COLS - 1, view->width - col);
+ if (opt_date && draw_date(view, time))
+ return TRUE;
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
- if (blame->commit)
- draw_text(view, blame->commit->author, max, TRUE, selected);
- col += AUTHOR_COLS;
- if (col >= view->width)
- return TRUE;
- wmove(view->win, lineno, col);
- }
+ if (opt_author &&
+ draw_field(view, LINE_MAIN_AUTHOR, author, opt_author_cols, TRUE))
+ return TRUE;
- {
- int max = MIN(ID_COLS - 1, view->width - col);
-
- if (!selected)
- wattrset(view->win, get_line_attr(LINE_BLAME_ID));
- if (blame->commit)
- draw_text(view, blame->commit->id, max, FALSE, -1);
- col += ID_COLS;
- if (col >= view->width)
- return TRUE;
- wmove(view->win, lineno, col);
- }
+ if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
+ return TRUE;
- col += draw_lineno(view, lineno, view->width - col, selected);
- if (col >= view->width)
+ if (draw_lineno(view, lineno))
return TRUE;
return TRUE;
- col += draw_text(view, blame->text, view->width - col, TRUE, selected);
+ draw_text(view, LINE_DEFAULT, blame->text, TRUE);
return TRUE;
}
return TRUE;
}
static char status_onbranch[SIZEOF_STR];
static struct status stage_status;
static enum line_type stage_line_type;
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
/* Get fields from the diff line:
* :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
}
static bool
}
static bool
-status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
+status_draw(struct view *view, struct line *line, unsigned int lineno)
{
struct status *status = line->data;
{
struct status *status = line->data;
+ enum line_type type;
char *text;
char *text;
- int col = 0;
-
- if (selected) {
- /* No attributes. */
-
- } else if (line->type == LINE_STAT_HEAD) {
- wattrset(view->win, get_line_attr(LINE_STAT_HEAD));
- wchgat(view->win, -1, 0, LINE_STAT_HEAD, NULL);
-
- } else if (!status && line->type != LINE_STAT_NONE) {
- wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
- wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
-
- } else {
- wattrset(view->win, get_line_attr(line->type));
- }
if (!status) {
switch (line->type) {
case LINE_STAT_STAGED:
if (!status) {
switch (line->type) {
case LINE_STAT_STAGED:
+ type = LINE_STAT_SECTION;
text = "Changes to be committed:";
break;
case LINE_STAT_UNSTAGED:
text = "Changes to be committed:";
break;
case LINE_STAT_UNSTAGED:
+ type = LINE_STAT_SECTION;
text = "Changed but not updated:";
break;
case LINE_STAT_UNTRACKED:
text = "Changed but not updated:";
break;
case LINE_STAT_UNTRACKED:
+ type = LINE_STAT_SECTION;
text = "Untracked files:";
break;
case LINE_STAT_NONE:
text = "Untracked files:";
break;
case LINE_STAT_NONE:
+ type = LINE_DEFAULT;
text = " (no files)";
break;
case LINE_STAT_HEAD:
text = " (no files)";
break;
case LINE_STAT_HEAD:
+ type = LINE_STAT_HEAD;
text = status_onbranch;
break;
text = status_onbranch;
break;
@@ -4043,15 +4087,16 @@ status_draw(struct view *view, struct line *line, unsigned int lineno, bool sele
return FALSE;
}
} else {
return FALSE;
}
} else {
- char buf[] = { status->status, ' ', ' ', ' ', 0 };
+ static char buf[] = { '?', ' ', ' ', ' ', 0 };
- col += draw_text(view, buf, view->width, TRUE, selected);
- if (!selected)
- wattrset(view->win, A_NORMAL);
+ buf[0] = status->status;
+ if (draw_text(view, line->type, buf, TRUE))
+ return TRUE;
+ type = LINE_DEFAULT;
text = status->new.name;
}
text = status->new.name;
}
- draw_text(view, text, view->width - col, TRUE, selected);
+ draw_text(view, type, text, TRUE);
return TRUE;
}
return TRUE;
}
}
stage_line_type = line->type;
}
stage_line_type = line->type;
+ stage_chunks = 0;
string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
}
string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
}
return TRUE;
}
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)
{
static enum request
stage_request(struct view *view, enum request request, struct line *line)
{
return REQ_NONE;
break;
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;
case REQ_EDIT:
if (!stage_status.new.name[0])
return request;
*/
static bool
*/
static bool
-main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
+main_draw(struct view *view, struct line *line, unsigned int lineno)
{
struct commit *commit = line->data;
{
struct commit *commit = line->data;
- enum line_type type;
- int col = 0;
if (!*commit->author)
return FALSE;
if (!*commit->author)
return FALSE;
- if (selected) {
- type = LINE_CURSOR;
- } else {
- type = LINE_MAIN_COMMIT;
- }
-
- if (opt_date) {
- col += draw_date(view, &commit->time, view->width, selected);
- if (col >= view->width)
- return TRUE;
- }
- if (type != LINE_CURSOR)
- wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
-
- if (opt_author) {
- int max_len;
-
- max_len = view->width - col;
- if (max_len > AUTHOR_COLS - 1)
- max_len = AUTHOR_COLS - 1;
- draw_text(view, commit->author, max_len, TRUE, selected);
- col += AUTHOR_COLS;
- if (col >= view->width)
- return TRUE;
- }
-
- if (opt_rev_graph && commit->graph_size) {
- size_t graph_size = view->width - col;
- size_t i;
-
- if (type != LINE_CURSOR)
- wattrset(view->win, get_line_attr(LINE_MAIN_REVGRAPH));
- wmove(view->win, lineno, col);
- if (graph_size > commit->graph_size)
- graph_size = commit->graph_size;
- /* Using waddch() instead of waddnstr() ensures that
- * they'll be rendered correctly for the cursor line. */
- for (i = 0; i < graph_size; i++)
- waddch(view->win, commit->graph[i]);
+ if (opt_date && draw_date(view, &commit->time))
+ return TRUE;
- col += commit->graph_size + 1;
- if (col >= view->width)
- return TRUE;
- waddch(view->win, ' ');
- }
- if (type != LINE_CURSOR)
- wattrset(view->win, A_NORMAL);
+ if (opt_author &&
+ draw_field(view, LINE_MAIN_AUTHOR, commit->author, opt_author_cols, TRUE))
+ return TRUE;
- wmove(view->win, lineno, col);
+ if (opt_rev_graph && commit->graph_size &&
+ draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
+ return TRUE;
if (opt_show_refs && commit->refs) {
size_t i = 0;
do {
if (opt_show_refs && commit->refs) {
size_t i = 0;
do {
- if (type == LINE_CURSOR)
- ;
- else if (commit->refs[i]->head)
- wattrset(view->win, get_line_attr(LINE_MAIN_HEAD));
+ enum line_type type;
+
+ if (commit->refs[i]->head)
+ type = LINE_MAIN_HEAD;
else if (commit->refs[i]->ltag)
else if (commit->refs[i]->ltag)
- wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
+ type = LINE_MAIN_LOCAL_TAG;
else if (commit->refs[i]->tag)
else if (commit->refs[i]->tag)
- wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
+ type = LINE_MAIN_TAG;
else if (commit->refs[i]->tracked)
else if (commit->refs[i]->tracked)
- wattrset(view->win, get_line_attr(LINE_MAIN_TRACKED));
+ type = LINE_MAIN_TRACKED;
else if (commit->refs[i]->remote)
else if (commit->refs[i]->remote)
- wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
+ type = LINE_MAIN_REMOTE;
else
else
- wattrset(view->win, get_line_attr(LINE_MAIN_REF));
-
- col += draw_text(view, "[", view->width - col, TRUE, selected);
- col += draw_text(view, commit->refs[i]->name, view->width - col,
- TRUE, selected);
- col += draw_text(view, "]", view->width - col, TRUE, selected);
- if (type != LINE_CURSOR)
- wattrset(view->win, A_NORMAL);
- col += draw_text(view, " ", view->width - col, TRUE, selected);
- if (col >= view->width)
+ type = LINE_MAIN_REF;
+
+ if (draw_text(view, type, "[", TRUE) ||
+ draw_text(view, type, commit->refs[i]->name, TRUE) ||
+ draw_text(view, type, "]", TRUE))
+ return TRUE;
+
+ if (draw_text(view, LINE_DEFAULT, " ", TRUE))
return TRUE;
} while (commit->refs[i++]->next);
}
return TRUE;
} while (commit->refs[i++]->next);
}
- if (type != LINE_CURSOR)
- wattrset(view->win, get_line_attr(type));
-
- draw_text(view, commit->title, view->width - col, TRUE, selected);
+ draw_text(view, LINE_DEFAULT, commit->title, TRUE);
return TRUE;
}
return TRUE;
}
*
* Returns the number of bytes to output from string to satisfy max_width. */
static size_t
*
* Returns the number of bytes to output from string to satisfy max_width. */
static size_t
-utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve)
+utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve)
{
const char *start = string;
const char *end = strchr(string, '\0');
unsigned char last_bytes = 0;
{
const char *start = string;
const char *end = strchr(string, '\0');
unsigned char last_bytes = 0;
- size_t width = 0;
+ size_t last_ucwidth = 0;
+ *width = 0;
*trimmed = 0;
while (string < end) {
*trimmed = 0;
while (string < end) {
@@ -5276,17 +5322,20 @@ utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve)
break;
ucwidth = unicode_width(unicode);
break;
ucwidth = unicode_width(unicode);
- width += ucwidth;
- if (width > max_width) {
+ *width += ucwidth;
+ if (*width > max_width) {
*trimmed = 1;
*trimmed = 1;
- if (reserve && width - ucwidth == max_width) {
+ *width -= ucwidth;
+ if (reserve && *width == max_width) {
string -= last_bytes;
string -= last_bytes;
+ *width -= last_ucwidth;
}
break;
}
string += bytes;
last_bytes = bytes;
}
break;
}
string += bytes;
last_bytes = bytes;
+ last_ucwidth = ucwidth;
}
return string - start;
}
return string - start;
if (load_git_config() == ERR)
die("Failed to load repo config.");
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. */
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"))
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);
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)) {
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])) {
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 {
} 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;
}
request = REQ_NONE;