bool prompt_menu(const char *prompt, const struct menu_item *items, int *selected) { enum input_status status = INPUT_OK; struct key key; int size = 0; while (items[size].text) size++; assert(size > 0); while (status == INPUT_OK) { const struct menu_item *item = &items[*selected]; char hotkey[] = { '[', (char) item->hotkey, ']', ' ', 0 }; int i; update_status("%s (%d of %d) %s%s", prompt, *selected + 1, size, item->hotkey ? hotkey : "", item->text); switch (get_input(COLS - 1, &key, FALSE)) { 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.data.bytes[0]) { *selected = i; status = INPUT_STOP; break; } } } report_clear(); return status != INPUT_CANCEL; }
rc_t report_destroy( p_report self ) { rc_t rc; if ( self != NULL ) { rc = report_clear( self ); free( self ); } else rc = RC( rcExe, rcNoTarg, rcConstructing, rcSelf, rcNull ); return rc; }
static enum request refs_request(struct view *view, enum request request, struct line *line) { struct reference *reference = line->data; switch (request) { case REQ_REFRESH: load_refs(TRUE); refresh_view(view); return REQ_NONE; case REQ_ENTER: { const struct ref *ref = reference->ref; const char *all_references_argv[] = { GIT_MAIN_LOG_CUSTOM(encoding_arg, commit_order_arg(), "", refs_is_all(reference) ? "--all" : ref->name, "") }; open_argv(view, &main_view, all_references_argv, NULL, OPEN_SPLIT); return REQ_NONE; } case REQ_JUMP_COMMIT: { int lineno; for (lineno = 0; lineno < view->lines; lineno++) { struct reference *reference = view->line[lineno].data; if (!strncasecmp(reference->ref->id, view->env->search, strlen(view->env->search))) { select_view_line(view, lineno); report_clear(); return REQ_NONE; } } } default: return request; } }
static bool 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_HALF_PAGE_UP: case REQ_MOVE_HALF_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: open_main_view(view, OPEN_DEFAULT); break; case REQ_VIEW_DIFF: open_diff_view(view, OPEN_DEFAULT); break; case REQ_VIEW_LOG: open_log_view(view, OPEN_DEFAULT); break; case REQ_VIEW_TREE: open_tree_view(view, OPEN_DEFAULT); break; case REQ_VIEW_HELP: open_help_view(view, OPEN_DEFAULT); break; case REQ_VIEW_REFS: open_refs_view(view, OPEN_DEFAULT); break; case REQ_VIEW_BLAME: open_blame_view(view, OPEN_DEFAULT); break; case REQ_VIEW_BLOB: open_blob_view(view, OPEN_DEFAULT); break; case REQ_VIEW_STATUS: open_status_view(view, OPEN_DEFAULT); break; case REQ_VIEW_STAGE: open_stage_view(view, NULL, 0, OPEN_DEFAULT); break; case REQ_VIEW_PAGER: open_pager_view(view, OPEN_DEFAULT); break; case REQ_VIEW_STASH: open_stash_view(view, 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 = nviews ? (current_view + 1) % nviews : current_view; 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 %s view", view->name); break; case REQ_BACK: report("Going back is not supported by the %s view", view->name); break; case REQ_MAXIMIZE: if (displayed_views() == 2) maximize_view(view, true); break; case REQ_OPTIONS: toggle_option(view); 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_MOVE_NEXT_MERGE: case REQ_MOVE_PREV_MERGE: report("Moving between merge commits is not supported by the %s view", view->name); 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; watch_unregister(&view->watch); view->parent = NULL; 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 run_prompt_command(struct view *view, char *cmd) { enum request request; if (cmd && 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 (cmd && iscommit(cmd)) { string_ncopy(view->env->search, cmd, strlen(cmd)); request = view_request(view, REQ_JUMP_COMMIT); if (request == REQ_JUMP_COMMIT) { report("Jumping to commits is not supported by the '%s' view", view->name); } } else if (cmd && strlen(cmd) == 1) { struct key_input input = { { cmd[0] } }; return get_keybinding(&view->ops->keymap, &input); } else if (cmd && cmd[0] == '!') { struct view *next = VIEW(REQ_VIEW_PAGER); const char *argv[SIZEOF_ARG]; int argc = 0; cmd++; /* When running random commands, initially show the * command in the title. However, it maybe later be * overwritten if a commit line is selected. */ string_ncopy(next->ref, cmd, strlen(cmd)); if (!argv_from_string(argv, &argc, cmd)) { report("Too many arguments"); } else if (!argv_format(view->env, &next->argv, argv, FALSE, TRUE)) { report("Argument formatting failed"); } else { next->dir = NULL; open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED); } } else if (cmd) { request = get_request(cmd); if (request != REQ_UNKNOWN) return request; char *args = strchr(cmd, ' '); if (args) { *args++ = 0; if (set_option(cmd, args) == SUCCESS) { request = !view->unrefreshable ? REQ_REFRESH : REQ_SCREEN_REDRAW; if (!strcmp(cmd, "color")) init_colors(); } } return request; } return REQ_NONE; }
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; }
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, 0, view->lines + 1) == SUCCESS) { if (!lineno) lineno = 1; select_view_line(view, lineno - 1); report_clear(); } else { report("Unable to parse '%s' as a line number", cmd); } } else if (iscommit(cmd)) { int lineno; if (!(view->ops->column_bits & view_column_bit(ID))) { report("Jumping to commits is not supported by the %s view", view->name); return REQ_NONE; } for (lineno = 0; lineno < view->lines; lineno++) { struct view_column_data column_data = {}; struct line *line = &view->line[lineno]; if (view->ops->get_column_data(view, line, &column_data) && column_data.id && !strncasecmp(column_data.id, cmd, cmdlen)) { string_ncopy(view->env->search, cmd, cmdlen); select_view_line(view, lineno); report_clear(); return REQ_NONE; } } report("Unable to find commit '%s'", view->env->search); return REQ_NONE; } 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, "save-display")) { const char *path = argv[1] ? argv[1] : "tig-display.txt"; if (!save_display(path)) report("Failed to save screen to %s", path); else report("Saved screen to %s", path); } else if (!strcmp(cmd, "exec")) { struct run_request req = { view->keymap, {}, argv + 1 }; enum status_code code = parse_run_request_flags(&req.flags, argv + 1); if (code != SUCCESS) { report("Failed to execute command: %s", get_status_message(code)); } else { return exec_run_request(view, &req); } } else if (!strcmp(cmd, "toggle")) { enum view_flag flags = VIEW_NO_FLAGS; enum status_code code = prompt_toggle(view, argv, &flags); const char *action = get_status_message(code); if (code != SUCCESS) { report("%s", action); return REQ_NONE; } prompt_update_display(flags); if (*action) report("%s", action); } else if (!strcmp(cmd, "script")) { if (is_script_executing()) { report("Scripts cannot be run from scripts"); } else if (!open_script(argv[1])) { report("Failed to open %s", argv[1]); } } else { struct key key = {}; enum status_code code; enum view_flag flags = VIEW_NO_FLAGS; /* Try :<key> */ key.modifiers.multibytes = 1; string_ncopy(key.data.bytes, cmd, cmdlen); request = get_keybinding(view->keymap, &key, 1); if (request != REQ_NONE) return request; /* Try :<command> */ request = get_request(cmd); if (request != REQ_UNKNOWN) return request; code = set_option(argv[0], argv_size(argv + 1), &argv[1]); if (code != SUCCESS) { report("%s", get_status_message(code)); return REQ_NONE; } if (!strcmp(cmd, "set")) { struct prompt_toggle *toggle; toggle = find_prompt_toggle(option_toggles, ARRAY_SIZE(option_toggles), "", argv[1], strlen(argv[1])); if (toggle) flags = toggle->flags; } if (flags) { prompt_update_display(flags); } else { request = view_can_refresh(view) ? REQ_REFRESH : REQ_SCREEN_REDRAW; if (!strcmp(cmd, "color")) init_colors(); resize_display(); redraw_display(TRUE); } } return REQ_NONE; }
static char * prompt_input(const char *prompt, struct input *input) { enum input_status status = INPUT_OK; unsigned char chars_length[SIZEOF_STR]; struct key key; size_t promptlen = strlen(prompt); int pos = 0, chars = 0; input->buf[pos] = 0; while (status == INPUT_OK || status == INPUT_SKIP) { update_status("%s%.*s", prompt, pos, input->buf); if (get_input(pos + promptlen, &key, FALSE) == OK) { int len = strlen(key.data.bytes); if (pos + len >= sizeof(input->buf)) { report("Input string too long"); return NULL; } string_ncopy_do(input->buf + pos, sizeof(input->buf) - pos, key.data.bytes, len); pos += len; chars_length[chars++] = len; status = input->handler(input, &key); if (status != INPUT_OK) { pos -= len; chars--; } else { int changed_pos = strlen(input->buf); if (changed_pos != pos) { pos = changed_pos; chars_length[chars - 1] = changed_pos - (pos - len); } } } else { status = input->handler(input, &key); if (status == INPUT_DELETE) { int len = chars_length[--chars]; pos -= len; status = INPUT_OK; } else { int changed_pos = strlen(input->buf); if (changed_pos != pos) { pos = changed_pos; chars_length[chars++] = changed_pos - pos; } } } input->buf[pos] = 0; } report_clear(); if (status == INPUT_CANCEL) return NULL; input->buf[pos++] = 0; return input->buf; }
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 {
static char * prompt_input(const char *prompt, input_handler handler, void *data) { enum input_status status = INPUT_OK; static char buf[SIZEOF_STR]; unsigned char chars_length[SIZEOF_STR]; struct key_input input; size_t promptlen = strlen(prompt); int pos = 0, chars = 0; buf[pos] = 0; while (status == INPUT_OK || status == INPUT_SKIP) { update_status("%s%.*s", prompt, pos, buf); switch (get_input(pos + promptlen, &input, FALSE)) { case KEY_RETURN: case KEY_ENTER: case '\n': status = pos ? INPUT_STOP : INPUT_CANCEL; break; case KEY_BACKSPACE: if (pos > 0) { int len = chars_length[--chars]; pos -= len; buf[pos] = 0; } else { status = INPUT_CANCEL; } break; case KEY_ESC: status = INPUT_CANCEL; break; default: if (pos >= sizeof(buf)) { report("Input string too long"); return NULL; } status = handler(data, buf, &input); if (status == INPUT_OK) { int len = strlen(input.data.bytes); string_ncopy_do(buf + pos, sizeof(buf) - pos, input.data.bytes, len); pos += len; chars_length[chars++] = len; } } } report_clear(); if (status == INPUT_CANCEL) return NULL; buf[pos++] = 0; return buf; }