static enum status_code prompt_toggle(struct view *view, const char *argv[], enum view_flag *flags) { const char *option = argv[1]; size_t optionlen = option ? strlen(option) : 0; struct option_info template; struct option_info *toggle; struct view_column *column; const char *column_name; if (!option) return error("%s", "No option name given to :toggle"); if (enum_equals_static("sort-field", option, optionlen) || enum_equals_static("sort-order", option, optionlen)) { if (!view_has_flags(view, VIEW_SORTABLE)) { return error("Sorting is not yet supported for the %s view", view->name); } else { bool sort_field = enum_equals_static("sort-field", option, optionlen); struct sort_state *sort = &view->sort; sort_view(view, sort_field); return success("set %s = %s", option, sort_field ? view_column_name(get_sort_field(view)) : sort->reverse ? "descending" : "ascending"); } } toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles), "", option); if (toggle) return prompt_toggle_option(view, argv, "", toggle, flags); for (column = view->columns; column; column = column->next) { toggle = find_column_option_info(column->type, &column->opt, option, &template, &column_name);
enum request pager_request(struct view *view, enum request request, struct line *line) { int split = 0; if (request != REQ_ENTER) return request; if (line->type == LINE_COMMIT && view_has_flags(view, VIEW_OPEN_DIFF)) { open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT); split = 1; } /* Always scroll the view even if it was split. That way * you can use Enter to scroll through the log view and * split open each commit diff. */ scroll_view(view, REQ_SCROLL_LINE_DOWN); /* FIXME: A minor workaround. Scrolling the view will call report_clear() * but if we are scrolling a non-current view this won't properly * update the view title. */ if (split) update_view_title(view); return REQ_NONE; }
static void add_pager_refs(struct view *view, const char *commit_id) { char buf[SIZEOF_STR]; const struct ref *list; size_t bufpos = 0; const char *sep = "Refs: "; list = get_ref_list(commit_id); if (!list) { if (view_has_flags(view, VIEW_ADD_DESCRIBE_REF) && refs_contain_tag()) add_line_text(view, sep, LINE_PP_REFS); return; } for (; list; list = list->next) { const struct ref *ref = list; const struct ref_format *fmt = get_ref_format(opt_reference_format, ref); if (!string_format_from(buf, &bufpos, "%s%s%s%s", sep, fmt->start, ref->name, fmt->end)) return; sep = ", "; } if (bufpos == 0) return; add_line_text(view, buf, LINE_PP_REFS); }
bool pager_common_read(struct view *view, const char *data, enum line_type type, struct line **line_ptr) { struct line *line; if (!data) return true; if (opt_wrap_lines) { line = pager_wrap_line(view, data, type); } else { line = add_line_text(view, data, type); } if (!line) return false; if (line_ptr) *line_ptr = line; if (line->type == LINE_COMMIT && view_has_flags(view, VIEW_ADD_PAGER_REFS)) add_pager_refs(view, data + STRING_SIZE("commit ")); return true; }
void pager_select(struct view *view, struct line *line) { if (line->type == LINE_COMMIT) { string_copy_rev_from_commit_line(view->env->commit, line->data); if (!view_has_flags(view, VIEW_NO_REF)) string_copy_rev(view->ref, view->env->commit); } }
static void add_pager_refs(struct view *view, const char *commit_id) { char buf[SIZEOF_STR]; struct ref_list *list; size_t bufpos = 0, i; const char *sep = "Refs: "; bool is_tag = FALSE; list = get_ref_list(commit_id); if (!list) { if (view_has_flags(view, VIEW_ADD_DESCRIBE_REF)) goto try_add_describe_ref; return; } for (i = 0; i < list->size; i++) { struct ref *ref = list->refs[i]; const char *fmt = ref->tag ? "%s[%s]" : ref->remote ? "%s<%s>" : "%s%s"; if (!string_format_from(buf, &bufpos, fmt, sep, ref->name)) return; sep = ", "; if (ref->tag) is_tag = TRUE; } if (!is_tag && view_has_flags(view, VIEW_ADD_DESCRIBE_REF)) { try_add_describe_ref: /* Add <tag>-g<commit_id> "fake" reference. */ if (!add_describe_ref(buf, &bufpos, commit_id, sep)) return; } if (bufpos == 0) return; add_line_text(view, buf, LINE_PP_REFS); }
static enum status_code prompt_toggle(struct view *view, const char *argv[], enum view_flag *flags) { const char *option = argv[1]; size_t optionlen = option ? strlen(option) : 0; struct prompt_toggle *toggle; struct view_column *column; if (!option) return error("%s", "No option name given to :toggle"); if (enum_equals_static("sort-field", option, optionlen) || enum_equals_static("sort-order", option, optionlen)) { if (!view_has_flags(view, VIEW_SORTABLE)) { return error("Sorting is not yet supported for the %s view", view->name); } else { bool sort_field = enum_equals_static("sort-field", option, optionlen); struct sort_state *sort = &view->sort; sort_view(view, sort_field); return success("set %s = %s", option, sort_field ? view_column_name(get_sort_field(view)) : sort->reverse ? "descending" : "ascending"); } } toggle = find_prompt_toggle(option_toggles, ARRAY_SIZE(option_toggles), "", option, optionlen); if (toggle) return prompt_toggle_option(view, argv, "", toggle, flags); #define DEFINE_COLUMN_OPTIONS_TOGGLE(name, type, flags) \ { #name, #type, flags, &opt->name }, #define DEFINE_COLUMN_OPTIONS_CHECK(name, id, options) \ if (column->type == VIEW_COLUMN_##id) { \ struct name##_options *opt = &column->opt.name; \ struct prompt_toggle toggles[] = { \ options(DEFINE_COLUMN_OPTIONS_TOGGLE) \ }; \ toggle = find_prompt_toggle(toggles, ARRAY_SIZE(toggles), #name, option, optionlen); \ if (toggle) \ return prompt_toggle_option(view, argv, #name, toggle, flags); \ } for (column = view->columns; column; column = column->next) { COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_CHECK); } return error("`:toggle %s` not supported", option); }
static void prompt_update_display(enum view_flag flags) { struct view *view; int i; if (flags & VIEW_RESET_DISPLAY) { resize_display(); redraw_display(TRUE); } foreach_displayed_view(view, i) { if (view_has_flags(view, flags) && view_can_refresh(view)) reload_view(view); else redraw_view(view); } }
static void log_select(struct view *view, struct line *line) { struct log_state *state = view->private; int last_lineno = state->last_lineno; if (!last_lineno || abs(last_lineno - line->lineno) > 1 || (state->last_type == LINE_COMMIT && last_lineno > line->lineno)) { struct line *commit_line = find_prev_line_by_type(view, line, LINE_COMMIT); if (commit_line) log_copy_rev(view, commit_line); } if (line->type == LINE_COMMIT && !view_has_flags(view, VIEW_NO_REF)) log_copy_rev(view, line); string_copy_rev(view->env->commit, view->ref); state->last_lineno = line->lineno; state->last_type = line->type; }
static enum view_flag prompt_toggle(struct view *view, const char *argv[], char msg[SIZEOF_STR]) { struct prompt_toggle option_toggles[] = { #define TOGGLE_OPTIONS(name, type, flags) { #name, #type, flags, &opt_ ## name }, OPTION_INFO(TOGGLE_OPTIONS) }; const char *option = argv[1]; size_t optionlen = option ? strlen(option) : 0; struct prompt_toggle *toggle; if (!option) { string_format_size(msg, SIZEOF_STR, "%s", "No option name given to :toggle"); return VIEW_NO_FLAGS; } if (enum_equals_static("sort-field", option, optionlen) || enum_equals_static("sort-order", option, optionlen)) { if (!view_has_flags(view, VIEW_SORTABLE)) { report("Sorting is not yet supported for the %s view", view->name); } else { bool sort_field = enum_equals_static("sort-field", option, optionlen); struct sort_state *sort = &view->sort; sort_view(view, sort_field); string_format_size(msg, SIZEOF_STR, "set %s = %s", option, sort_field ? enum_name(view_column_type_map->entries[get_sort_field(view)]) : sort->reverse ? "descending" : "ascending"); } return VIEW_NO_FLAGS; } toggle = find_prompt_toggle(option_toggles, ARRAY_SIZE(option_toggles), option, optionlen); if (toggle) return prompt_toggle_option(view, argv, toggle, msg); string_format_size(msg, SIZEOF_STR, "`:toggle %s` not supported", option); return VIEW_NO_FLAGS; }
static enum request view_request(struct view *view, enum request request) { if (!view || !view->lines) return request; if (request == REQ_ENTER && !opt_focus_child && view_has_flags(view, VIEW_SEND_CHILD_ENTER)) { struct view *child = display[1]; if (forward_request_to_child(child, request)) { view_request(child, request); return REQ_NONE; } } if (request == REQ_REFRESH && !view_can_refresh(view)) { report("This view can not be refreshed"); return REQ_NONE; } return view->ops->request(view, request, &view->line[view->pos.lineno]); }
static int view_driver(struct view *view, enum request request) { int i; if (request == REQ_NONE) return TRUE; if (request >= REQ_RUN_REQUESTS) { request = open_run_request(view, request); // exit quickly rather than going through view_request and back if (request == REQ_QUIT) return FALSE; } request = view_request(view, request); if (request == REQ_NONE) return TRUE; switch (request) { case REQ_MOVE_UP: case REQ_MOVE_DOWN: case REQ_MOVE_PAGE_UP: case REQ_MOVE_PAGE_DOWN: case REQ_MOVE_FIRST_LINE: case REQ_MOVE_LAST_LINE: move_view(view, request); break; case REQ_SCROLL_FIRST_COL: case REQ_SCROLL_LEFT: case REQ_SCROLL_RIGHT: case REQ_SCROLL_LINE_DOWN: case REQ_SCROLL_LINE_UP: case REQ_SCROLL_PAGE_DOWN: case REQ_SCROLL_PAGE_UP: case REQ_SCROLL_WHEEL_DOWN: case REQ_SCROLL_WHEEL_UP: scroll_view(view, request); break; case REQ_VIEW_GREP: open_grep_view(view); break; case REQ_VIEW_MAIN: case REQ_VIEW_DIFF: case REQ_VIEW_LOG: case REQ_VIEW_TREE: case REQ_VIEW_HELP: case REQ_VIEW_BRANCH: case REQ_VIEW_BLAME: case REQ_VIEW_BLOB: case REQ_VIEW_STATUS: case REQ_VIEW_STAGE: case REQ_VIEW_PAGER: case REQ_VIEW_STASH: open_view(view, request, OPEN_DEFAULT); break; case REQ_NEXT: case REQ_PREVIOUS: if (view->parent) { int line; view = view->parent; line = view->pos.lineno; view_request(view, request); move_view(view, request); if (view_is_displayed(view)) update_view_title(view); if (line != view->pos.lineno) view_request(view, REQ_ENTER); } else { move_view(view, request); } break; case REQ_VIEW_NEXT: { int nviews = displayed_views(); int next_view = (current_view + 1) % nviews; if (next_view == current_view) { report("Only one view is displayed"); break; } current_view = next_view; /* Blur out the title of the previous view. */ update_view_title(view); report_clear(); break; } case REQ_REFRESH: report("Refreshing is not supported by the %s view", view->name); break; case REQ_PARENT: report("Moving to parent is not supported by the the %s view", view->name); break; case REQ_BACK: report("Going back is not supported for by %s view", view->name); break; case REQ_MAXIMIZE: if (displayed_views() == 2) maximize_view(view, TRUE); break; case REQ_OPTIONS: case REQ_TOGGLE_LINENO: case REQ_TOGGLE_DATE: case REQ_TOGGLE_AUTHOR: case REQ_TOGGLE_FILENAME: case REQ_TOGGLE_GRAPHIC: case REQ_TOGGLE_REV_GRAPH: case REQ_TOGGLE_REFS: case REQ_TOGGLE_CHANGES: case REQ_TOGGLE_IGNORE_SPACE: case REQ_TOGGLE_ID: case REQ_TOGGLE_FILES: case REQ_TOGGLE_TITLE_OVERFLOW: case REQ_TOGGLE_FILE_SIZE: case REQ_TOGGLE_UNTRACKED_DIRS: case REQ_TOGGLE_VERTICAL_SPLIT: { char action[SIZEOF_STR] = ""; enum view_flag flags = toggle_option(view, request, action); if (flags == VIEW_FLAG_RESET_DISPLAY) { resize_display(); redraw_display(TRUE); } else { foreach_displayed_view(view, i) { if (view_has_flags(view, flags) && !view->unrefreshable) reload_view(view); else redraw_view(view); } } if (*action) report("%s", action); } break; case REQ_TOGGLE_SORT_FIELD: case REQ_TOGGLE_SORT_ORDER: report("Sorting is not yet supported for the %s view", view->name); break; case REQ_DIFF_CONTEXT_UP: case REQ_DIFF_CONTEXT_DOWN: report("Changing the diff context is not yet supported for the %s view", view->name); break; case REQ_SEARCH: case REQ_SEARCH_BACK: search_view(view, request); break; case REQ_FIND_NEXT: case REQ_FIND_PREV: find_next(view, request); break; case REQ_STOP_LOADING: foreach_view(view, i) { if (view->pipe) report("Stopped loading the %s view", view->name), end_update(view, TRUE); } break; case REQ_SHOW_VERSION: report("tig-%s (built %s)", TIG_VERSION, __DATE__); return TRUE; case REQ_SCREEN_REDRAW: redraw_display(TRUE); break; case REQ_EDIT: report("Nothing to edit"); break; case REQ_ENTER: report("Nothing to enter"); break; case REQ_VIEW_CLOSE: /* XXX: Mark closed views by letting view->prev point to the * view itself. Parents to closed view should never be * followed. */ if (view->prev && view->prev != view) { maximize_view(view->prev, TRUE); view->prev = view; break; } /* Fall-through */ case REQ_QUIT: return FALSE; default: report("Unknown key, press %s for help", get_view_key(view, REQ_VIEW_HELP)); return TRUE; } return TRUE; }
static enum request open_run_request(struct view *view, enum request request) { struct run_request *req = get_run_request(request); const char **argv = NULL; bool confirmed = FALSE; request = REQ_NONE; if (!req) { report("Unknown run request"); return request; } if (argv_format(view->env, &argv, req->argv, FALSE, TRUE)) { if (req->internal) { char cmd[SIZEOF_STR]; if (argv_to_string(argv, cmd, sizeof(cmd), " ")) { request = run_prompt_command(view, cmd); } } else { confirmed = !req->confirm; if (req->confirm) { char cmd[SIZEOF_STR], prompt[SIZEOF_STR]; const char *and_exit = req->exit ? " and exit" : ""; if (argv_to_string(argv, cmd, sizeof(cmd), " ") && string_format(prompt, "Run `%s`%s?", cmd, and_exit) && prompt_yesno(prompt)) { confirmed = TRUE; } } if (confirmed && argv_remove_quotes(argv)) { if (req->silent) io_run_bg(argv); else open_external_viewer(argv, NULL, !req->exit, ""); } } } if (argv) argv_free(argv); free(argv); if (request == REQ_NONE) { if (req->confirm && !confirmed) request = REQ_NONE; else if (req->exit) request = REQ_QUIT; else if (view_has_flags(view, VIEW_REFRESH) && !view->unrefreshable) request = REQ_REFRESH; } return request; }
enum request run_prompt_command(struct view *view, const char *argv[]) { enum request request; const char *cmd = argv[0]; size_t cmdlen = cmd ? strlen(cmd) : 0; if (!cmd) return REQ_NONE; if (string_isnumber(cmd)) { int lineno = view->pos.lineno + 1; if (parse_int(&lineno, cmd, 1, view->lines + 1) == SUCCESS) { select_view_line(view, lineno - 1); report_clear(); } else { report("Unable to parse '%s' as a line number", cmd); } } else if (iscommit(cmd)) { string_ncopy(view->env->search, cmd, cmdlen); return REQ_JUMP_COMMIT; } else if (cmdlen > 1 && (cmd[0] == '/' || cmd[0] == '?')) { char search[SIZEOF_STR]; if (!argv_to_string(argv, search, sizeof(search), " ")) { report("Failed to copy search string"); return REQ_NONE; } if (!strcmp(search + 1, view->env->search)) return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV; string_ncopy(view->env->search, search + 1, strlen(search + 1)); return cmd[0] == '/' ? REQ_SEARCH : REQ_SEARCH_BACK; } else if (cmdlen > 1 && cmd[0] == '!') { struct view *next = &pager_view; bool copied; /* Trim the leading '!'. */ argv[0] = cmd + 1; copied = argv_format(view->env, &next->argv, argv, FALSE, TRUE); argv[0] = cmd; if (!copied) { report("Argument formatting failed"); } else { /* When running random commands, initially show the * command in the title. However, it maybe later be * overwritten if a commit line is selected. */ argv_to_string(next->argv, next->ref, sizeof(next->ref), " "); next->dir = NULL; open_pager_view(view, OPEN_PREPARED | OPEN_WITH_STDERR); } } else if (!strcmp(cmd, "toggle")) { char action[SIZEOF_STR] = ""; enum view_flag flags = prompt_toggle(view, argv, action); int i; if (flags & VIEW_RESET_DISPLAY) { resize_display(); redraw_display(TRUE); } foreach_displayed_view(view, i) { if (view_has_flags(view, flags) && !view->unrefreshable) reload_view(view); else redraw_view(view); } if (*action) report("%s", action); } else {