Merge pull request #457 from vivien/text-variable
[tig.git] / src / prompt.c
blob63bdf5c25b5bd710a9780674914285e75cf06bfb
1 /* Copyright (c) 2006-2015 Jonas Fonseca <jonas.fonseca@gmail.com>
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2 of
6 * the License, or (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include "tig/tig.h"
15 #include "tig/repo.h"
16 #include "tig/view.h"
17 #include "tig/draw.h"
18 #include "tig/display.h"
19 #include "tig/options.h"
20 #include "tig/prompt.h"
21 #include "tig/pager.h"
22 #include "tig/types.h"
24 #ifdef HAVE_READLINE
25 #include <readline/readline.h>
26 #include <readline/history.h>
27 #endif /* HAVE_READLINE */
29 static char *
30 prompt_input(const char *prompt, struct input *input)
32 enum input_status status = INPUT_OK;
33 unsigned char chars_length[SIZEOF_STR];
34 struct key key;
35 size_t promptlen = strlen(prompt);
36 int pos = 0, chars = 0;
37 int last_buf_length = promptlen ? -1 : promptlen;
39 input->buf[0] = 0;
41 while (status == INPUT_OK || status == INPUT_SKIP) {
42 int buf_length = strlen(input->buf) + promptlen;
43 int offset = pos || buf_length != last_buf_length ? pos + promptlen : -1;
45 last_buf_length = buf_length;
46 if (offset >= 0)
47 update_status("%s%.*s", prompt, pos, input->buf);
49 if (get_input(offset, &key) == OK) {
50 int len = strlen(key.data.bytes);
52 if (pos + len >= sizeof(input->buf)) {
53 report("Input string too long");
54 return NULL;
57 string_ncopy_do(input->buf + pos, sizeof(input->buf) - pos, key.data.bytes, len);
58 pos += len;
59 chars_length[chars++] = len;
60 status = input->handler(input, &key);
61 if (status != INPUT_OK) {
62 pos -= len;
63 chars--;
64 } else {
65 int changed_pos = strlen(input->buf);
67 if (changed_pos != pos) {
68 pos = changed_pos;
69 chars_length[chars - 1] = changed_pos - (pos - len);
72 } else {
73 status = input->handler(input, &key);
74 if (status == INPUT_DELETE) {
75 int len = chars_length[--chars];
77 pos -= len;
78 status = INPUT_OK;
79 } else {
80 int changed_pos = strlen(input->buf);
82 if (changed_pos != pos) {
83 pos = changed_pos;
84 chars_length[chars++] = changed_pos - pos;
88 input->buf[pos] = 0;
91 report_clear();
93 if (status == INPUT_CANCEL)
94 return NULL;
96 input->buf[pos++] = 0;
98 return input->buf;
101 enum input_status
102 prompt_default_handler(struct input *input, struct key *key)
104 switch (key_to_value(key)) {
105 case KEY_RETURN:
106 case KEY_ENTER:
107 case '\n':
108 return *input->buf || input->allow_empty ? INPUT_STOP : INPUT_CANCEL;
110 case KEY_BACKSPACE:
111 return *input->buf ? INPUT_DELETE : INPUT_CANCEL;
113 case KEY_ESC:
114 return INPUT_CANCEL;
116 default:
117 return INPUT_SKIP;
121 static enum input_status
122 prompt_yesno_handler(struct input *input, struct key *key)
124 unsigned long c = key_to_unicode(key);
126 if (c == 'y' || c == 'Y')
127 return INPUT_STOP;
128 if (c == 'n' || c == 'N')
129 return INPUT_CANCEL;
130 return prompt_default_handler(input, key);
133 bool
134 prompt_yesno(const char *prompt)
136 char prompt2[SIZEOF_STR];
137 struct input input = { prompt_yesno_handler, false, NULL };
139 if (!string_format(prompt2, "%s [Yy/Nn]", prompt))
140 return false;
142 return !!prompt_input(prompt2, &input);
145 struct incremental_input {
146 struct input input;
147 input_handler handler;
148 bool edit_mode;
151 static enum input_status
152 read_prompt_handler(struct input *input, struct key *key)
154 struct incremental_input *incremental = (struct incremental_input *) input;
156 if (incremental->edit_mode && !key->modifiers.multibytes)
157 return prompt_default_handler(input, key);
159 if (!unicode_width(key_to_unicode(key), 8))
160 return INPUT_SKIP;
162 if (!incremental->handler)
163 return INPUT_OK;
165 return incremental->handler(input, key);
168 char *
169 read_prompt_incremental(const char *prompt, bool edit_mode, bool allow_empty, input_handler handler, void *data)
171 static struct incremental_input incremental = { { read_prompt_handler } };
173 incremental.input.allow_empty = allow_empty;
174 incremental.input.data = data;
175 incremental.handler = handler;
176 incremental.edit_mode = edit_mode;
178 return prompt_input(prompt, (struct input *) &incremental);
181 #ifdef HAVE_READLINE
182 static void
183 readline_display(void)
185 update_status("%s%s", rl_display_prompt, rl_line_buffer);
186 wmove(status_win, 0, strlen(rl_display_prompt) + rl_point);
187 wrefresh(status_win);
190 static char *
191 readline_variable_generator(const char *text, int state)
193 static const char *vars[] = {
194 #define FORMAT_VAR(type, name, ifempty, initval) "%(" #name ")",
195 ARGV_ENV_INFO(FORMAT_VAR)
196 #undef FORMAT_VAR
197 NULL
200 static int index, len;
201 const char *name;
202 char *variable = NULL; /* No match */
204 /* If it is a new word to complete, initialize */
205 if (!state) {
206 index = 0;
207 len = strlen(text);
210 /* Return the next name which partially matches */
211 while ((name = vars[index])) {
212 index++;
214 /* Complete or format a variable */
215 if (strncmp(name, text, len) == 0) {
216 if (strlen(name) > len)
217 variable = strdup(name);
218 else
219 variable = argv_format_arg(&argv_env, text);
220 break;
224 return variable;
227 static char *
228 readline_action_generator(const char *text, int state)
230 static const char *actions[] = {
231 "!",
232 "source",
233 "color",
234 "bind",
235 "set",
236 "toggle",
237 "save-display",
238 "save-options",
239 "exec",
240 #define REQ_GROUP(help)
241 #define REQ_(req, help) #req
242 REQ_INFO,
243 #undef REQ_GROUP
244 #undef REQ_
245 NULL
248 static int index, len;
249 const char *name;
250 char *match = NULL; /* No match */
252 /* If it is a new word to complete, initialize */
253 if (!state) {
254 index = 0;
255 len = strlen(text);
258 /* Return the next name which partially matches */
259 while ((name = actions[index])) {
260 name = enum_name(name);
261 index++;
263 if (strncmp(name, text, len) == 0) {
264 /* Ignore exact completion */
265 if (strlen(name) > len)
266 match = strdup(name);
267 break;
271 return match;
274 static char *
275 readline_set_generator(const char *text, int state)
277 static const char *words[] = {
278 #define DEFINE_OPTION_NAME(name, type, flags) #name " = ",
279 OPTION_INFO(DEFINE_OPTION_NAME)
280 #undef DEFINE_OPTION_NAME
281 NULL
284 static int index, len;
285 const char *name;
286 char *match = NULL; /* No match */
288 /* If it is a new word to complete, initialize */
289 if (!state) {
290 index = 0;
291 len = strlen(text);
294 /* Return the next name which partially matches */
295 while ((name = words[index])) {
296 name = enum_name(name);
297 index++;
299 if (strncmp(name, text, len) == 0) {
300 /* Ignore exact completion */
301 if (strlen(name) > len)
302 match = strdup(name);
303 break;
307 return match;
310 static char *
311 readline_toggle_generator(const char *text, int state)
313 static const char **words;
314 static int index, len;
315 const char *name;
316 char *match = NULL; /* No match */
318 if (!words) {
319 /* TODO: Only complete column options that are defined
320 * for the view. */
322 #define DEFINE_OPTION_WORD(name, type, flags) argv_append(&words, #name);
323 #define DEFINE_COLUMN_OPTIONS_WORD(name, type, flags) #name,
324 #define DEFINE_COLUMN_OPTIONS_WORDS(name, id, options) \
325 if (VIEW_COLUMN_##id != VIEW_COLUMN_SECTION) { \
326 const char *vars[] = { \
327 options(DEFINE_COLUMN_OPTIONS_WORD) \
328 }; \
329 char buf[SIZEOF_STR]; \
330 int i; \
331 for (i = 0; i < ARRAY_SIZE(vars); i++) { \
332 if (enum_name_prefixed(buf, sizeof(buf), #name, vars[i])) \
333 argv_append(&words, buf); \
337 OPTION_INFO(DEFINE_OPTION_WORD)
338 COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_WORDS);
341 /* If it is a new word to complete, initialize */
342 if (!state) {
343 index = 0;
344 len = strlen(text);
347 /* Return the next name which partially matches */
348 while ((name = words[index])) {
349 name = enum_name(name);
350 index++;
352 if (strncmp(name, text, len) == 0) {
353 /* Ignore exact completion */
354 if (strlen(name) > len)
355 match = strdup(name);
356 break;
360 return match;
363 static int
364 readline_getc(FILE *stream)
366 return get_input_char();
369 static char **
370 readline_completion(const char *text, int start, int end)
372 /* Do not append a space after a completion */
373 rl_completion_suppress_append = 1;
376 * If the word is at the start of the line,
377 * then it is a tig action to complete.
379 if (start == 0)
380 return rl_completion_matches(text, readline_action_generator);
383 * If the line begins with "toggle", then we complete toggle options.
385 if (start >= 7 && strncmp(rl_line_buffer, "toggle ", 7) == 0)
386 return rl_completion_matches(text, readline_toggle_generator);
389 * If the line begins with "set", then we complete set options.
390 * (unless it is already completed)
392 if (start >= 4 && strncmp(rl_line_buffer, "set ", 4) == 0 &&
393 !strchr(rl_line_buffer, '='))
394 return rl_completion_matches(text, readline_set_generator);
397 * Otherwise it might be a variable name...
399 if (strncmp(text, "%(", 2) == 0)
400 return rl_completion_matches(text, readline_variable_generator);
403 * ... or finally fall back to filename completion.
405 return NULL;
408 static void
409 readline_display_matches(char **matches, int num_matches, int max_length)
411 unsigned int i;
413 wmove(status_win, 0, 0);
414 waddstr(status_win, "matches: ");
416 /* matches[0] is the incomplete word */
417 for (i = 1; i < num_matches + 1; ++i) {
418 waddstr(status_win, matches[i]);
419 waddch(status_win, ' ');
422 wgetch(status_win);
423 wrefresh(status_win);
426 static void
427 readline_init(void)
429 /* Allow conditional parsing of the ~/.inputrc file. */
430 rl_readline_name = "tig";
432 /* Word break caracters (we removed '(' to match variables) */
433 rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{";
435 /* Custom display function */
436 rl_redisplay_function = readline_display;
437 rl_getc_function = readline_getc;
439 /* Completion support */
440 rl_attempted_completion_function = readline_completion;
442 rl_completion_display_matches_hook = readline_display_matches;
445 char *
446 read_prompt(const char *prompt)
448 static char *line = NULL;
450 if (line) {
451 free(line);
452 line = NULL;
455 line = readline(prompt);
457 if (line && !*line) {
458 free(line);
459 line = NULL;
462 if (line)
463 add_history(line);
465 return line;
468 void
469 prompt_init(void)
471 readline_init();
473 #else
474 char *
475 read_prompt(const char *prompt)
477 return read_prompt_incremental(prompt, true, false, NULL, NULL);
480 void
481 prompt_init(void)
484 #endif /* HAVE_READLINE */
486 bool
487 prompt_menu(const char *prompt, const struct menu_item *items, int *selected)
489 enum input_status status = INPUT_OK;
490 struct key key;
491 int size = 0;
493 while (items[size].text)
494 size++;
496 assert(size > 0);
498 while (status == INPUT_OK) {
499 const struct menu_item *item = &items[*selected];
500 char hotkey[] = { '[', (char) item->hotkey, ']', ' ', 0 };
501 int i;
503 update_status("%s (%d of %d) %s%s", prompt, *selected + 1, size,
504 item->hotkey ? hotkey : "", item->text);
506 switch (get_input(COLS - 1, &key)) {
507 case KEY_RETURN:
508 case KEY_ENTER:
509 case '\n':
510 status = INPUT_STOP;
511 break;
513 case KEY_LEFT:
514 case KEY_UP:
515 *selected = *selected - 1;
516 if (*selected < 0)
517 *selected = size - 1;
518 break;
520 case KEY_RIGHT:
521 case KEY_DOWN:
522 *selected = (*selected + 1) % size;
523 break;
525 case KEY_ESC:
526 status = INPUT_CANCEL;
527 break;
529 default:
530 for (i = 0; items[i].text; i++)
531 if (items[i].hotkey == key.data.bytes[0]) {
532 *selected = i;
533 status = INPUT_STOP;
534 break;
539 report_clear();
541 return status != INPUT_CANCEL;
544 static struct option_info option_toggles[] = {
545 #define DEFINE_OPTION_TOGGLES(name, type, flags) { #name, STRING_SIZE(#name), #type, &opt_ ## name, flags },
546 OPTION_INFO(DEFINE_OPTION_TOGGLES)
549 static bool
550 find_arg(const char *argv[], const char *arg)
552 int i;
554 for (i = 0; argv && argv[i]; i++)
555 if (!strcmp(argv[i], arg))
556 return true;
557 return false;
560 static enum status_code
561 prompt_toggle_option(struct view *view, const char *argv[], const char *prefix,
562 struct option_info *toggle, enum view_flag *flags)
564 char name[SIZEOF_STR];
566 if (!enum_name_prefixed(name, sizeof(name), prefix, toggle->name))
567 return error("Failed to toggle option %s", toggle->name);
569 *flags = toggle->flags;
571 if (!strcmp(toggle->type, "bool")) {
572 bool *opt = toggle->value;
574 *opt = !*opt;
575 if (opt == &opt_mouse)
576 enable_mouse(*opt);
577 return success("set %s = %s", name, *opt ? "yes" : "no");
579 } else if (!strncmp(toggle->type, "enum", 4)) {
580 const char *type = toggle->type + STRING_SIZE("enum ");
581 enum author *opt = toggle->value;
582 const struct enum_map *map = find_enum_map(type);
584 *opt = (*opt + 1) % map->size;
585 return success("set %s = %s", name, enum_name(map->entries[*opt].name));
587 } else if (!strcmp(toggle->type, "int")) {
588 const char *arg = argv[2] ? argv[2] : "1";
589 int diff = atoi(arg);
590 int *opt = toggle->value;
592 if (!diff)
593 diff = *arg == '-' ? -1 : 1;
595 if (opt == &opt_diff_context && *opt < 0)
596 *opt = -*opt;
597 if (opt == &opt_diff_context && diff < 0) {
598 if (!*opt)
599 return error("Diff context cannot be less than zero");
600 if (*opt < -diff)
601 diff = -*opt;
604 if (strstr(name, "commit-title-overflow")) {
605 *opt = *opt ? -*opt : 50;
606 if (*opt < 0)
607 return success("set %s = no", name);
608 diff = 0;
611 *opt += diff;
612 return success("set %s = %d", name, *opt);
614 } else if (!strcmp(toggle->type, "double")) {
615 const char *arg = argv[2] ? argv[2] : "1.0";
616 double *opt = toggle->value;
617 int sign = 1;
618 double diff;
620 if (*arg == '-') {
621 sign = -1;
622 arg++;
625 if (parse_step(&diff, arg) != SUCCESS)
626 diff = strtod(arg, NULL);
628 *opt += sign * diff;
629 return success("set %s = %.2f", name, *opt);
631 } else if (!strcmp(toggle->type, "const char **")) {
632 const char ***opt = toggle->value;
633 bool found = true;
634 int i;
636 if (argv_size(argv) <= 2) {
637 argv_free(*opt);
638 (*opt)[0] = NULL;
639 return SUCCESS;
642 for (i = 2; argv[i]; i++) {
643 if (!find_arg(*opt, argv[i])) {
644 found = false;
645 break;
649 if (found) {
650 int next, pos;
652 for (next = 0, pos = 0; (*opt)[pos]; pos++) {
653 const char *arg = (*opt)[pos];
655 if (find_arg(argv + 2, arg)) {
656 free((void *) arg);
657 continue;
659 (*opt)[next++] = arg;
662 (*opt)[next] = NULL;
664 } else if (!argv_copy(opt, argv + 2)) {
665 return ERROR_OUT_OF_MEMORY;
667 return SUCCESS;
669 } else {
670 return error("Unsupported `:toggle %s` (%s)", name, toggle->type);
674 static enum status_code
675 prompt_toggle(struct view *view, const char *argv[], enum view_flag *flags)
677 const char *option = argv[1];
678 size_t optionlen = option ? strlen(option) : 0;
679 struct option_info template;
680 struct option_info *toggle;
681 struct view_column *column;
682 const char *column_name;
684 if (!option)
685 return error("%s", "No option name given to :toggle");
687 if (enum_equals_static("sort-field", option, optionlen) ||
688 enum_equals_static("sort-order", option, optionlen)) {
689 if (!view_has_flags(view, VIEW_SORTABLE)) {
690 return error("Sorting is not yet supported for the %s view", view->name);
691 } else {
692 bool sort_field = enum_equals_static("sort-field", option, optionlen);
693 struct sort_state *sort = &view->sort;
695 sort_view(view, sort_field);
696 return success("set %s = %s", option,
697 sort_field ? view_column_name(get_sort_field(view))
698 : sort->reverse ? "descending" : "ascending");
702 toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles), "", option);
703 if (toggle)
704 return prompt_toggle_option(view, argv, "", toggle, flags);
706 for (column = view->columns; column; column = column->next) {
707 toggle = find_column_option_info(column->type, &column->opt, option, &template, &column_name);
708 if (toggle)
709 return prompt_toggle_option(view, argv, column_name, toggle, flags);
712 return error("`:toggle %s` not supported", option);
715 static void
716 prompt_update_display(enum view_flag flags)
718 struct view *view;
719 int i;
721 if (flags & VIEW_RESET_DISPLAY) {
722 resize_display();
723 redraw_display(true);
726 foreach_displayed_view(view, i) {
727 if (view_has_flags(view, flags) && view_can_refresh(view))
728 reload_view(view);
729 else
730 redraw_view(view);
734 enum request
735 run_prompt_command(struct view *view, const char *argv[])
737 enum request request;
738 const char *cmd = argv[0];
739 size_t cmdlen = cmd ? strlen(cmd) : 0;
741 if (!cmd)
742 return REQ_NONE;
744 if (string_isnumber(cmd)) {
745 int lineno = view->pos.lineno + 1;
747 if (parse_int(&lineno, cmd, 0, view->lines + 1) == SUCCESS) {
748 if (!lineno)
749 lineno = 1;
750 select_view_line(view, lineno - 1);
751 report_clear();
752 } else {
753 report("Unable to parse '%s' as a line number", cmd);
755 } else if (iscommit(cmd)) {
756 int lineno;
758 if (!(view->ops->column_bits & view_column_bit(ID))) {
759 report("Jumping to commits is not supported by the %s view", view->name);
760 return REQ_NONE;
763 for (lineno = 0; lineno < view->lines; lineno++) {
764 struct view_column_data column_data = {0};
765 struct line *line = &view->line[lineno];
767 if (view->ops->get_column_data(view, line, &column_data) &&
768 column_data.id &&
769 !strncasecmp(column_data.id, cmd, cmdlen)) {
770 string_ncopy(view->env->search, cmd, cmdlen);
771 select_view_line(view, lineno);
772 report_clear();
773 return REQ_NONE;
777 report("Unable to find commit '%s'", view->env->search);
778 return REQ_NONE;
780 } else if (cmdlen > 1 && (cmd[0] == '/' || cmd[0] == '?')) {
781 char search[SIZEOF_STR];
783 if (!argv_to_string(argv, search, sizeof(search), " ")) {
784 report("Failed to copy search string");
785 return REQ_NONE;
788 if (!strcmp(search + 1, view->env->search))
789 return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV;
791 string_ncopy(view->env->search, search + 1, strlen(search + 1));
792 return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV;
794 } else if (cmdlen > 1 && cmd[0] == '!') {
795 struct view *next = &pager_view;
796 bool copied;
798 /* Trim the leading '!'. */
799 argv[0] = cmd + 1;
800 copied = argv_format(view->env, &next->argv, argv, false, true);
801 argv[0] = cmd;
803 if (!copied) {
804 report("Argument formatting failed");
805 } else {
806 /* When running random commands, initially show the
807 * command in the title. However, it maybe later be
808 * overwritten if a commit line is selected. */
809 argv_to_string(next->argv, next->ref, sizeof(next->ref), " ");
811 next->dir = NULL;
812 open_pager_view(view, OPEN_PREPARED | OPEN_WITH_STDERR);
815 } else if (!strcmp(cmd, "save-display")) {
816 const char *path = argv[1] ? argv[1] : "tig-display.txt";
818 if (!save_display(path))
819 report("Failed to save screen to %s", path);
820 else
821 report("Saved screen to %s", path);
823 } else if (!strcmp(cmd, "save-options")) {
824 const char *path = argv[1] ? argv[1] : "tig-options.txt";
825 enum status_code code = save_options(path);
827 if (code != SUCCESS)
828 report("Failed to save options: %s", get_status_message(code));
829 else
830 report("Saved options to %s", path);
832 } else if (!strcmp(cmd, "exec")) {
833 struct run_request req = { view->keymap, {0}, argv + 1 };
834 enum status_code code = parse_run_request_flags(&req.flags, argv + 1);
836 if (code != SUCCESS) {
837 report("Failed to execute command: %s", get_status_message(code));
838 } else {
839 return exec_run_request(view, &req);
842 } else if (!strcmp(cmd, "toggle")) {
843 enum view_flag flags = VIEW_NO_FLAGS;
844 enum status_code code = prompt_toggle(view, argv, &flags);
845 const char *action = get_status_message(code);
847 if (code != SUCCESS) {
848 report("%s", action);
849 return REQ_NONE;
852 prompt_update_display(flags);
854 if (*action)
855 report("%s", action);
857 } else if (!strcmp(cmd, "script")) {
858 enum status_code code = open_script(argv[1]);
860 if (code != SUCCESS)
861 report("%s", get_status_message(code));
862 return REQ_NONE;
864 } else {
865 struct key key = {{0}};
866 enum status_code code;
867 enum view_flag flags = VIEW_NO_FLAGS;
869 /* Try :<key> */
870 key.modifiers.multibytes = 1;
871 string_ncopy(key.data.bytes, cmd, cmdlen);
872 request = get_keybinding(view->keymap, &key, 1, NULL);
873 if (request != REQ_UNKNOWN)
874 return request;
876 /* Try :<command> */
877 request = get_request(cmd);
878 if (request != REQ_UNKNOWN)
879 return request;
881 code = set_option(argv[0], argv_size(argv + 1), &argv[1]);
882 if (code != SUCCESS) {
883 report("%s", get_status_message(code));
884 return REQ_NONE;
887 if (!strcmp(cmd, "set")) {
888 struct option_info *toggle;
890 toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles),
891 "", argv[1]);
893 if (toggle)
894 flags = toggle->flags;
897 if (flags) {
898 prompt_update_display(flags);
900 } else {
901 request = view_can_refresh(view) ? REQ_REFRESH : REQ_SCREEN_REDRAW;
902 if (!strcmp(cmd, "color"))
903 init_colors();
904 resize_display();
905 redraw_display(true);
909 return REQ_NONE;
912 enum request
913 exec_run_request(struct view *view, struct run_request *req)
915 const char **argv = NULL;
916 bool confirmed = false;
917 enum request request = REQ_NONE;
918 char cmd[SIZEOF_STR];
919 const char *req_argv[SIZEOF_ARG];
920 int req_argc = 0;
922 if (!argv_to_string(req->argv, cmd, sizeof(cmd), " ")
923 || !argv_from_string_no_quotes(req_argv, &req_argc, cmd)
924 || !argv_format(view->env, &argv, req_argv, false, true)) {
925 report("Failed to format arguments");
926 return REQ_NONE;
929 if (req->flags.internal) {
930 request = run_prompt_command(view, argv);
932 } else {
933 confirmed = !req->flags.confirm;
935 if (req->flags.confirm) {
936 char cmd[SIZEOF_STR], prompt[SIZEOF_STR];
937 const char *and_exit = req->flags.exit ? " and exit" : "";
939 if (argv_to_string_quoted(argv, cmd, sizeof(cmd), " ") &&
940 string_format(prompt, "Run `%s`%s?", cmd, and_exit) &&
941 prompt_yesno(prompt)) {
942 confirmed = true;
946 if (confirmed)
947 open_external_viewer(argv, repo.cdup, req->flags.silent,
948 !req->flags.exit, false, "");
951 if (argv)
952 argv_free(argv);
953 free(argv);
955 if (request == REQ_NONE) {
956 if (req->flags.confirm && !confirmed)
957 request = REQ_NONE;
959 else if (req->flags.exit)
960 request = REQ_QUIT;
962 else if (!req->flags.internal && watch_dirty(&view->watch))
963 request = REQ_REFRESH;
967 return request;
970 enum request
971 open_prompt(struct view *view)
973 char *cmd = read_prompt(":");
974 const char *argv[SIZEOF_ARG] = { NULL };
975 int argc = 0;
977 if (cmd && !argv_from_string(argv, &argc, cmd)) {
978 report("Too many arguments");
979 return REQ_NONE;
982 return run_prompt_command(view, argv);
985 /* vim: set ts=8 sw=8 noexpandtab: */