X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=tig.c;h=aec50bca39ff98d3d8358789c6a70f676e2d5bfb;hb=deefc9e2b8508d621aed566adfa833f4c402d3a0;hp=e0d0640b97d73b1b26058724c19a02afea487ff6;hpb=60c440586f21704fcfec49d5c4dcfb8b840c737f;p=tig.git diff --git a/tig.c b/tig.c index e0d0640..aec50bc 100644 --- a/tig.c +++ b/tig.c @@ -188,20 +188,6 @@ string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen) #define string_add(dst, from, src) \ string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src)) -static size_t -string_expand_length(const char *line, int tabsize) -{ - size_t size, pos; - - for (pos = 0; line[pos]; pos++) { - if (line[pos] == '\t' && tabsize > 0) - size += tabsize - (size % tabsize); - else - size++; - } - return size; -} - static void string_expand(char *dst, size_t dstlen, const char *src, int tabsize) { @@ -291,7 +277,7 @@ struct enum_map { #define ENUM_MAP(name, value) { name, STRING_SIZE(name), value } static bool -map_enum_do(struct enum_map *map, size_t map_size, int *value, const char *name) +map_enum_do(const struct enum_map *map, size_t map_size, int *value, const char *name) { size_t namelen = strlen(name); int i; @@ -412,6 +398,8 @@ io_open(struct io *io, const char *name) { init_io(io, NULL, IO_FD); io->pipe = *name ? open(name, O_RDONLY) : STDIN_FILENO; + if (io->pipe == -1) + io->error = errno; return io->pipe != -1; } @@ -571,7 +559,7 @@ io_error(struct io *io) return io->error; } -static bool +static char * io_strerror(struct io *io) { return strerror(io->error); @@ -676,20 +664,24 @@ io_write(struct io *io, const void *buf, size_t bufsize) } static bool -run_io_buf(const char **argv, char buf[], size_t bufsize) +io_read_buf(struct io *io, char buf[], size_t bufsize) { - struct io io = {}; bool error; - if (!run_io_rd(&io, argv, FORMAT_NONE)) - return FALSE; + io->buf = io->bufpos = buf; + io->bufalloc = bufsize; + error = !io_get(io, '\n', TRUE) && io_error(io); + io->buf = NULL; - io.buf = io.bufpos = buf; - io.bufalloc = bufsize; - error = !io_get(&io, '\n', TRUE) && io_error(&io); - io.buf = NULL; + return done_io(io) || error; +} - return done_io(&io) || error; +static bool +run_io_buf(const char **argv, char buf[], size_t bufsize) +{ + struct io io = {}; + + return run_io_rd(&io, argv, FORMAT_NONE) && io_read_buf(&io, buf, bufsize); } static int @@ -834,7 +826,7 @@ struct request_info { const char *help; }; -static struct request_info req_info[] = { +static const struct request_info req_info[] = { #define REQ_GROUP(help) { 0, NULL, 0, (help) }, #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) } REQ_INFO @@ -1057,7 +1049,7 @@ struct keybinding { enum request request; }; -static struct keybinding default_keybindings[] = { +static const struct keybinding default_keybindings[] = { /* View switching */ { 'm', REQ_VIEW_MAIN }, { 'd', REQ_VIEW_DIFF }, @@ -1143,7 +1135,7 @@ enum keymap { #undef KEYMAP_ }; -static struct enum_map keymap_table[] = { +static const struct enum_map keymap_table[] = { #define KEYMAP_(name) ENUM_MAP(#name, KEYMAP_##name) KEYMAP_INFO #undef KEYMAP_ @@ -1198,7 +1190,7 @@ struct key { int value; }; -static struct key key_table[] = { +static const struct key key_table[] = { { "Enter", KEY_RETURN }, { "Space", ' ' }, { "Backspace", KEY_BACKSPACE }, @@ -1276,7 +1268,7 @@ get_key(enum request request) buf[pos] = 0; for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) { - struct keybinding *keybinding = &default_keybindings[i]; + const struct keybinding *keybinding = &default_keybindings[i]; if (keybinding->request != request) continue; @@ -1360,7 +1352,11 @@ add_builtin_run_requests(void) * User config file handling. */ -static struct enum_map color_map[] = { +static int config_lineno; +static bool config_errors; +static const char *config_msg; + +static const struct enum_map color_map[] = { #define COLOR_MAP(name) ENUM_MAP(#name, COLOR_##name) COLOR_MAP(DEFAULT), COLOR_MAP(BLACK), @@ -1373,7 +1369,7 @@ static struct enum_map color_map[] = { COLOR_MAP(YELLOW), }; -static struct enum_map attr_map[] = { +static const struct enum_map attr_map[] = { #define ATTR_MAP(name) ENUM_MAP(#name, A_##name) ATTR_MAP(NORMAL), ATTR_MAP(BLINK), @@ -1384,12 +1380,31 @@ static struct enum_map attr_map[] = { ATTR_MAP(UNDERLINE), }; -#define set_color(color, name) map_enum(color, color_map, name) #define set_attribute(attr, name) map_enum(attr, attr_map, name) -static int config_lineno; -static bool config_errors; -static const char *config_msg; +static int +parse_int(int *opt, const char *arg, int min, int max) +{ + int value = atoi(arg); + + if (min <= value && value <= max) { + *opt = value; + return OK; + } + + config_msg = "Integer value out of bound"; + return ERR; +} + +static bool +set_color(int *color, const char *name) +{ + if (map_enum(color, color_map, name)) + return TRUE; + if (!prefixcmp(name, "color")) + return parse_int(color, name + 5, 0, 255) == OK; + return FALSE; +} /* Wants: object fgcolor bgcolor [attribute] */ static int @@ -1404,7 +1419,7 @@ option_color_command(int argc, const char *argv[]) info = get_line_info(argv[0]); if (!info) { - static struct enum_map obsolete[] = { + static const struct enum_map obsolete[] = { ENUM_MAP("main-delim", LINE_DELIMITER), ENUM_MAP("main-date", LINE_DATE), ENUM_MAP("main-author", LINE_AUTHOR), @@ -1439,16 +1454,6 @@ static int parse_bool(bool *opt, const char *arg) return OK; } -static int -parse_int(int *opt, const char *arg, int min, int max) -{ - int value = atoi(arg); - - if (min <= value && value <= max) - *opt = value; - return OK; -} - static int parse_string(char *opt, const char *arg, size_t optsize) { @@ -1542,7 +1547,7 @@ option_bind_command(int argc, const char *argv[]) request = get_request(argv[2]); if (request == REQ_NONE) { - static struct enum_map obsolete[] = { + static const struct enum_map obsolete[] = { ENUM_MAP("cherry-pick", REQ_NONE), ENUM_MAP("screen-resize", REQ_NONE), ENUM_MAP("tree-parent", REQ_PARENT), @@ -1659,11 +1664,8 @@ load_options(void) add_builtin_run_requests(); - if (!tigrc_system) { - if (!string_format(buf, "%s/tigrc", SYSCONFDIR)) - return ERR; - tigrc_system = buf; - } + if (!tigrc_system) + tigrc_system = SYSCONFDIR "/tigrc"; load_option_file(tigrc_system); if (!tigrc_user) { @@ -2241,10 +2243,46 @@ toggle_view_option(bool *option, const char *help) report("%sabling %s", *option ? "En" : "Dis", help); } +static void +maximize_view(struct view *view) +{ + memset(display, 0, sizeof(display)); + current_view = 0; + display[current_view] = view; + resize_display(); + redraw_display(FALSE); + report(""); +} + + /* * Navigation */ +static bool +goto_view_line(struct view *view, unsigned long offset, unsigned long lineno) +{ + if (lineno >= view->lines) + lineno = view->lines > 0 ? view->lines - 1 : 0; + + if (offset > lineno || offset + view->height <= lineno) { + unsigned long half = view->height / 2; + + if (lineno > half) + offset = lineno - half; + else + offset = 0; + } + + if (offset != view->offset || lineno != view->lineno) { + view->offset = offset; + view->lineno = lineno; + return TRUE; + } + + return FALSE; +} + /* Scrolling backend */ static void do_scroll_view(struct view *view, int lines) @@ -2456,20 +2494,18 @@ static void search_view(struct view *view, enum request request); static void select_view_line(struct view *view, unsigned long lineno) { - if (lineno - view->offset >= view->height) { - view->offset = lineno; - view->lineno = lineno; - if (view_is_displayed(view)) - redraw_view(view); + unsigned long old_lineno = view->lineno; + unsigned long old_offset = view->offset; - } else { - unsigned long old_lineno = view->lineno - view->offset; - - view->lineno = lineno; + if (goto_view_line(view, view->offset, lineno)) { if (view_is_displayed(view)) { - draw_view_line(view, old_lineno); - draw_view_line(view, view->lineno - view->offset); - wnoutrefresh(view->win); + if (old_offset != view->offset) { + redraw_view(view); + } else { + draw_view_line(view, old_lineno - view->offset); + draw_view_line(view, view->lineno - view->offset); + wnoutrefresh(view->win); + } } else { view->ops->select(view, &view->line[view->lineno]); } @@ -2661,26 +2697,11 @@ restore_view_position(struct view *view) return FALSE; } - if (view->p_lineno >= view->lines) { - view->p_lineno = view->lines > 0 ? view->lines - 1 : 0; - if (view->p_offset >= view->p_lineno) { - unsigned long half = view->height / 2; - - if (view->p_lineno > half) - view->p_offset = view->p_lineno - half; - else - view->p_offset = 0; - } - } - - if (view_is_displayed(view) && - view->offset != view->p_offset && - view->lineno != view->p_lineno) + if (goto_view_line(view, view->p_offset, view->p_lineno) && + view_is_displayed(view)) werase(view->win); - view->offset = view->p_offset; view->yoffset = view->p_yoffset; - view->lineno = view->p_lineno; view->p_restore = FALSE; return TRUE; @@ -3218,7 +3239,7 @@ view_driver(struct view *view, enum request request) case REQ_MAXIMIZE: if (displayed_views() == 2) - open_view(view, VIEW_REQ(view), OPEN_DEFAULT); + maximize_view(view); break; case REQ_TOGGLE_LINENO: @@ -3282,13 +3303,8 @@ view_driver(struct view *view, enum request request) * followed. */ if (view->parent && view->parent->parent != view->parent) { - memset(display, 0, sizeof(display)); - current_view = 0; - display[current_view] = view->parent; + maximize_view(view->parent); view->parent = view; - resize_display(); - redraw_display(FALSE); - report(""); break; } /* Fall-through */ @@ -3377,11 +3393,11 @@ select_commit_parent_handler(void *data, char *buf, int c) } static bool -select_commit_parent(const char *id, char rev[SIZEOF_REV]) +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, NULL + "git", "rev-list", "-1", "--parents", id, "--", path, NULL }; int parents; @@ -3392,7 +3408,10 @@ select_commit_parent(const char *id, char rev[SIZEOF_REV]) return FALSE; } else if (parents == 0) { - report("The selected commit has no parents"); + if (path) + report("Path '%s' does not exist in the parent", path); + else + report("The selected commit has no parents"); return FALSE; } @@ -4170,6 +4189,7 @@ struct blame_commit { struct blame { struct blame_commit *commit; + unsigned long lineno; char text[1]; }; @@ -4233,14 +4253,16 @@ parse_blame_commit(struct view *view, const char *text, int *blamed) { struct blame_commit *commit; struct blame *blame; - const char *pos = text + SIZEOF_REV - 1; + const char *pos = text + SIZEOF_REV - 2; + size_t orig_lineno = 0; size_t lineno; size_t group; - if (strlen(text) <= SIZEOF_REV || *pos != ' ') + if (strlen(text) <= SIZEOF_REV || pos[1] != ' ') return NULL; - if (!parse_number(&pos, &lineno, 1, view->lines) || + if (!parse_number(&pos, &orig_lineno, 1, 9999999) || + !parse_number(&pos, &lineno, 1, view->lines) || !parse_number(&pos, &group, 1, view->lines - lineno + 1)) return NULL; @@ -4254,6 +4276,7 @@ parse_blame_commit(struct view *view, const char *text, int *blamed) blame = line->data; blame->commit = commit; + blame->lineno = orig_lineno + group - 1; line->dirty = 1; } @@ -4281,14 +4304,15 @@ blame_read_file(struct view *view, const char *line, bool *read_file) return FALSE; } else { - size_t linelen = string_expand_length(line, opt_tab_size); + size_t linelen = strlen(line); struct blame *blame = malloc(sizeof(*blame) + linelen); if (!blame) return FALSE; blame->commit = NULL; - string_expand(blame->text, linelen + 1, line, opt_tab_size); + strncpy(blame->text, line, linelen); + blame->text[linelen] = 0; return add_line_data(view, blame, LINE_BLAME_ID) != NULL; } } @@ -4364,6 +4388,7 @@ blame_draw(struct view *view, struct line *line, unsigned int lineno) struct blame *blame = line->data; struct tm *time = NULL; const char *id = NULL, *author = NULL; + char text[SIZEOF_STR]; if (blame->commit && *blame->commit->filename) { id = blame->commit->id; @@ -4383,22 +4408,59 @@ blame_draw(struct view *view, struct line *line, unsigned int lineno) if (draw_lineno(view, lineno)) return TRUE; - draw_text(view, LINE_DEFAULT, blame->text, TRUE); + string_expand(text, sizeof(text), blame->text, opt_tab_size); + draw_text(view, LINE_DEFAULT, text, TRUE); return TRUE; } static bool -check_blame_commit(struct blame *blame) +check_blame_commit(struct blame *blame, bool check_null_id) { if (!blame->commit) report("Commit data not loaded yet"); - else if (!strcmp(blame->commit->id, NULL_ID)) + else if (check_null_id && !strcmp(blame->commit->id, NULL_ID)) report("No commit exist for the selected line"); else return TRUE; return FALSE; } +static void +setup_blame_parent_line(struct view *view, struct blame *blame) +{ + const char *diff_tree_argv[] = { + "git", "diff-tree", "-U0", blame->commit->id, + "--", blame->commit->filename, NULL + }; + struct io io = {}; + int parent_lineno = -1; + int blamed_lineno = -1; + char *line; + + if (!run_io(&io, diff_tree_argv, NULL, IO_RD)) + return; + + while ((line = io_get(&io, '\n', TRUE))) { + if (*line == '@') { + char *pos = strchr(line, '+'); + + parent_lineno = atoi(line + 4); + if (pos) + blamed_lineno = atoi(pos + 1); + + } else if (*line == '+' && parent_lineno != -1) { + if (blame->lineno == blamed_lineno - 1 && + !strcmp(blame->text, line + 1)) { + view->lineno = parent_lineno ? parent_lineno - 1 : 0; + break; + } + blamed_lineno++; + } + } + + done_io(&io); +} + static enum request blame_request(struct view *view, enum request request, struct line *line) { @@ -4407,23 +4469,28 @@ blame_request(struct view *view, enum request request, struct line *line) switch (request) { case REQ_VIEW_BLAME: - if (check_blame_commit(blame)) { + if (check_blame_commit(blame, TRUE)) { string_copy(opt_ref, blame->commit->id); + string_copy(opt_file, blame->commit->filename); + if (blame->lineno) + view->lineno = blame->lineno; open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH); } break; case REQ_PARENT: - if (check_blame_commit(blame) && - select_commit_parent(blame->commit->id, opt_ref)) + if (check_blame_commit(blame, TRUE) && + select_commit_parent(blame->commit->id, opt_ref, + blame->commit->filename)) { + string_copy(opt_file, blame->commit->filename); + setup_blame_parent_line(view, blame); open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH); + } break; case REQ_ENTER: - if (!blame->commit) { - report("No commit loaded yet"); + if (!check_blame_commit(blame, FALSE)) break; - } if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) && !strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref)) @@ -4705,6 +4772,55 @@ status_restore(struct view *view) view->p_restore = FALSE; } +static void +status_update_onbranch(void) +{ + static const char *paths[][2] = { + { "rebase-apply/rebasing", "Rebasing" }, + { "rebase-apply/applying", "Applying mailbox" }, + { "rebase-apply/", "Rebasing mailbox" }, + { "rebase-merge/interactive", "Interactive rebase" }, + { "rebase-merge/", "Rebase merge" }, + { "MERGE_HEAD", "Merging" }, + { "BISECT_LOG", "Bisecting" }, + { "HEAD", "On branch" }, + }; + char buf[SIZEOF_STR]; + struct stat stat; + int i; + + if (is_initial_commit()) { + string_copy(status_onbranch, "Initial commit"); + return; + } + + for (i = 0; i < ARRAY_SIZE(paths); i++) { + char *head = opt_head; + + if (!string_format(buf, "%s/%s", opt_git_dir, paths[i][0]) || + lstat(buf, &stat) < 0) + continue; + + if (!*opt_head) { + struct io io = {}; + + if (string_format(buf, "%s/rebase-merge/head-name", opt_git_dir) && + io_open(&io, buf) && + io_read_buf(&io, buf, sizeof(buf))) { + head = chomp_string(buf); + if (!prefixcmp(head, "refs/heads/")) + head += STRING_SIZE("refs/heads/"); + } + } + + if (!string_format(status_onbranch, "%s %s", paths[i][1], head)) + string_copy(status_onbranch, opt_head); + return; + } + + string_copy(status_onbranch, "Not currently on any branch"); +} + /* First parse staged info using git-diff-index(1), then parse unstaged * info using git-diff-files(1), and finally untracked files using * git-ls-files(1). */ @@ -4714,12 +4830,7 @@ status_open(struct view *view) reset_view(view); add_line_data(view, NULL, LINE_STAT_HEAD); - if (is_initial_commit()) - string_copy(status_onbranch, "Initial commit"); - else if (!*opt_head) - string_copy(status_onbranch, "Not currently on any branch"); - else if (!string_format(status_onbranch, "On branch %s", opt_head)) - return FALSE; + status_update_onbranch(); run_io_bg(update_index_argv); @@ -4792,6 +4903,15 @@ status_draw(struct view *view, struct line *line, unsigned int lineno) return TRUE; } +static enum request +status_load_error(struct view *view, struct view *stage, const char *path) +{ + if (displayed_views() == 2 || display[current_view] != view) + maximize_view(view); + report("Failed to load '%s': %s", path, io_strerror(&stage->io)); + return REQ_NONE; +} + static enum request status_enter(struct view *view, struct line *line) { @@ -4819,7 +4939,7 @@ status_enter(struct view *view, struct line *line) }; if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH)) - return REQ_QUIT; + return status_load_error(view, stage, newpath); } else { const char *index_show_argv[] = { "git", "diff-index", "--root", "--patch-with-stat", @@ -4828,7 +4948,7 @@ status_enter(struct view *view, struct line *line) }; if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH)) - return REQ_QUIT; + return status_load_error(view, stage, newpath); } if (status) @@ -4845,7 +4965,7 @@ status_enter(struct view *view, struct line *line) }; if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH)) - return REQ_QUIT; + return status_load_error(view, stage, newpath); if (status) info = "Unstaged changes to %s"; else @@ -4864,7 +4984,7 @@ status_enter(struct view *view, struct line *line) } if (!prepare_update_file(stage, newpath)) - return REQ_QUIT; + return status_load_error(view, stage, newpath); info = "Untracked file %s"; break; @@ -5123,8 +5243,7 @@ status_request(struct view *view, enum request request, struct line *line) /* After returning the status view has been split to * show the stage view. No further reloading is * necessary. */ - status_enter(view, line); - return REQ_NONE; + return status_enter(view, line); case REQ_REFRESH: /* Simply reload the view. */ @@ -6555,7 +6674,7 @@ static int load_refs(void) { static const char *ls_remote_argv[SIZEOF_ARG] = { - "git", "ls-remote", ".", NULL + "git", "ls-remote", opt_git_dir, NULL }; static bool init = FALSE; @@ -6575,6 +6694,23 @@ load_refs(void) return run_io_load(ls_remote_argv, "\t", read_ref); } +static void +set_remote_branch(const char *name, const char *value, size_t valuelen) +{ + if (!strcmp(name, ".remote")) { + string_ncopy(opt_remote, value, valuelen); + + } else if (*opt_remote && !strcmp(name, ".merge")) { + size_t from = strlen(opt_remote); + + if (!prefixcmp(value, "refs/heads/")) + value += STRING_SIZE("refs/heads/"); + + if (!string_format_from(opt_remote, &from, "/%s", value)) + opt_remote[0] = 0; + } +} + static void set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **)) { @@ -6591,16 +6727,43 @@ set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **)) warn("Option 'tig.%s': %s", name, config_msg); } +static void +set_work_tree(const char *value) +{ + char cwd[SIZEOF_STR]; + + if (!getcwd(cwd, sizeof(cwd))) + die("Failed to get cwd path: %s", strerror(errno)); + if (chdir(opt_git_dir) < 0) + die("Failed to chdir(%s): %s", strerror(errno)); + if (!getcwd(opt_git_dir, sizeof(opt_git_dir))) + die("Failed to get git path: %s", strerror(errno)); + if (chdir(cwd) < 0) + die("Failed to chdir(%s): %s", cwd, strerror(errno)); + if (chdir(value) < 0) + die("Failed to chdir(%s): %s", value, strerror(errno)); + if (!getcwd(cwd, sizeof(cwd))) + die("Failed to get cwd path: %s", strerror(errno)); + if (setenv("GIT_WORK_TREE", cwd, TRUE) < 0) + die("Failed to set GIT_WORK_TREE to '%s'", cwd); + if (setenv("GIT_DIR", opt_git_dir, TRUE) < 0) + die("Failed to set GIT_DIR to '%s'", opt_git_dir); + opt_is_inside_work_tree = TRUE; +} + static int read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen) { if (!strcmp(name, "i18n.commitencoding")) string_ncopy(opt_encoding, value, valuelen); - if (!strcmp(name, "core.editor")) + else if (!strcmp(name, "core.editor")) string_ncopy(opt_editor, value, valuelen); - if (!prefixcmp(name, "tig.color.")) + else if (!strcmp(name, "core.worktree")) + set_work_tree(value); + + else if (!prefixcmp(name, "tig.color.")) set_repo_config_option(name + 10, value, option_color_command); else if (!prefixcmp(name, "tig.bind.")) @@ -6609,27 +6772,9 @@ read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen else if (!prefixcmp(name, "tig.")) set_repo_config_option(name + 4, value, option_set_command); - /* branch..remote */ - if (*opt_head && - !strncmp(name, "branch.", 7) && - !strncmp(name + 7, opt_head, strlen(opt_head)) && - !strcmp(name + 7 + strlen(opt_head), ".remote")) - string_ncopy(opt_remote, value, valuelen); - - if (*opt_head && *opt_remote && - !strncmp(name, "branch.", 7) && - !strncmp(name + 7, opt_head, strlen(opt_head)) && - !strcmp(name + 7 + strlen(opt_head), ".merge")) { - size_t from = strlen(opt_remote); - - if (!prefixcmp(value, "refs/heads/")) { - value += STRING_SIZE("refs/heads/"); - valuelen -= STRING_SIZE("refs/heads/"); - } - - if (!string_format_from(opt_remote, &from, "/%s", value)) - opt_remote[0] = 0; - } + else if (*opt_head && !prefixcmp(name, "branch.") && + !strncmp(name + 7, opt_head, strlen(opt_head))) + set_remote_branch(name + 7 + strlen(opt_head), value, valuelen); return OK; } @@ -6885,7 +7030,17 @@ main(int argc, const char *argv[]) { char *cmd = read_prompt(":"); - if (cmd) { + if (cmd && isdigit(*cmd)) { + int lineno = view->lineno + 1; + + if (parse_int(&lineno, cmd, 1, view->lines + 1) == OK) { + select_view_line(view, lineno - 1); + report(""); + } else { + report("Unable to parse '%s' as a line number", cmd); + } + + } else if (cmd) { struct view *next = VIEW(REQ_VIEW_PAGER); const char *argv[SIZEOF_ARG] = { "git" }; int argc = 1;