tig-2.1.1
[tig.git] / src / prompt.c
blob340205219ae42c7fd5cf6e1d58d61d09fad02eeb
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/view.h"
16 #include "tig/draw.h"
17 #include "tig/display.h"
18 #include "tig/options.h"
19 #include "tig/prompt.h"
20 #include "tig/pager.h"
21 #include "tig/types.h"
23 #ifdef HAVE_READLINE
24 #include <readline/readline.h>
25 #include <readline/history.h>
26 #endif /* HAVE_READLINE */
28 static char *
29 prompt_input(const char *prompt, struct input *input)
31 enum input_status status = INPUT_OK;
32 unsigned char chars_length[SIZEOF_STR];
33 struct key key;
34 size_t promptlen = strlen(prompt);
35 int pos = 0, chars = 0;
36 int last_buf_length = promptlen ? -1 : promptlen;
38 input->buf[0] = 0;
40 while (status == INPUT_OK || status == INPUT_SKIP) {
41 int buf_length = strlen(input->buf) + promptlen;
42 int offset = pos || buf_length != last_buf_length ? pos + promptlen : -1;
44 last_buf_length = buf_length;
45 if (offset >= 0)
46 update_status("%s%.*s", prompt, pos, input->buf);
48 if (get_input(offset, &key) == OK) {
49 int len = strlen(key.data.bytes);
51 if (pos + len >= sizeof(input->buf)) {
52 report("Input string too long");
53 return NULL;
56 string_ncopy_do(input->buf + pos, sizeof(input->buf) - pos, key.data.bytes, len);
57 pos += len;
58 chars_length[chars++] = len;
59 status = input->handler(input, &key);
60 if (status != INPUT_OK) {
61 pos -= len;
62 chars--;
63 } else {
64 int changed_pos = strlen(input->buf);
66 if (changed_pos != pos) {
67 pos = changed_pos;
68 chars_length[chars - 1] = changed_pos - (pos - len);
71 } else {
72 status = input->handler(input, &key);
73 if (status == INPUT_DELETE) {
74 int len = chars_length[--chars];
76 pos -= len;
77 status = INPUT_OK;
78 } else {
79 int changed_pos = strlen(input->buf);
81 if (changed_pos != pos) {
82 pos = changed_pos;
83 chars_length[chars++] = changed_pos - pos;
87 input->buf[pos] = 0;
90 report_clear();
92 if (status == INPUT_CANCEL)
93 return NULL;
95 input->buf[pos++] = 0;
97 return input->buf;
100 enum input_status
101 prompt_default_handler(struct input *input, struct key *key)
103 switch (key_to_value(key)) {
104 case KEY_RETURN:
105 case KEY_ENTER:
106 case '\n':
107 return *input->buf ? INPUT_STOP : INPUT_CANCEL;
109 case KEY_BACKSPACE:
110 return *input->buf ? INPUT_DELETE : INPUT_CANCEL;
112 case KEY_ESC:
113 return INPUT_CANCEL;
115 default:
116 return INPUT_SKIP;
120 static enum input_status
121 prompt_yesno_handler(struct input *input, struct key *key)
123 unsigned long c = key_to_unicode(key);
125 if (c == 'y' || c == 'Y')
126 return INPUT_STOP;
127 if (c == 'n' || c == 'N')
128 return INPUT_CANCEL;
129 return prompt_default_handler(input, key);
132 bool
133 prompt_yesno(const char *prompt)
135 char prompt2[SIZEOF_STR];
136 struct input input = { prompt_yesno_handler, NULL };
138 if (!string_format(prompt2, "%s [Yy/Nn]", prompt))
139 return FALSE;
141 return !!prompt_input(prompt2, &input);
144 struct incremental_input {
145 struct input input;
146 input_handler handler;
147 bool edit_mode;
150 static enum input_status
151 read_prompt_handler(struct input *input, struct key *key)
153 struct incremental_input *incremental = (struct incremental_input *) input;
155 if (incremental->edit_mode && !key->modifiers.multibytes)
156 return prompt_default_handler(input, key);
158 if (!unicode_width(key_to_unicode(key), 8))
159 return INPUT_SKIP;
161 if (!incremental->handler)
162 return INPUT_OK;
164 return incremental->handler(input, key);
167 char *
168 read_prompt_incremental(const char *prompt, bool edit_mode, input_handler handler, void *data)
170 static struct incremental_input incremental = { { read_prompt_handler } };
172 incremental.input.data = data;
173 incremental.handler = handler;
174 incremental.edit_mode = edit_mode;
176 return prompt_input(prompt, (struct input *) &incremental);
179 #ifdef HAVE_READLINE
180 static void
181 readline_display(void)
183 update_status("%s%s", rl_display_prompt, rl_line_buffer);
184 wmove(status_win, 0, strlen(rl_display_prompt) + rl_point);
185 wrefresh(status_win);
188 static char *
189 readline_variable_generator(const char *text, int state)
191 static const char *vars[] = {
192 #define FORMAT_VAR(type, name, ifempty, initval) "%(" #name ")",
193 ARGV_ENV_INFO(FORMAT_VAR)
194 #undef FORMAT_VAR
195 NULL
198 static int index, len;
199 const char *name;
200 char *variable = NULL; /* No match */
202 /* If it is a new word to complete, initialize */
203 if (!state) {
204 index = 0;
205 len = strlen(text);
208 /* Return the next name which partially matches */
209 while ((name = vars[index])) {
210 index++;
212 /* Complete or format a variable */
213 if (strncmp(name, text, len) == 0) {
214 if (strlen(name) > len)
215 variable = strdup(name);
216 else
217 variable = argv_format_arg(&argv_env, text);
218 break;
222 return variable;
225 static char *
226 readline_action_generator(const char *text, int state)
228 static const char *actions[] = {
229 "!",
230 "source",
231 "color",
232 "bind",
233 "set",
234 "toggle",
235 "save-display",
236 "save-options",
237 "exec",
238 #define REQ_GROUP(help)
239 #define REQ_(req, help) #req
240 REQ_INFO,
241 #undef REQ_GROUP
242 #undef REQ_
243 NULL
246 static int index, len;
247 const char *name;
248 char *match = NULL; /* No match */
250 /* If it is a new word to complete, initialize */
251 if (!state) {
252 index = 0;
253 len = strlen(text);
256 /* Return the next name which partially matches */
257 while ((name = actions[index])) {
258 name = enum_name(name);
259 index++;
261 if (strncmp(name, text, len) == 0) {
262 /* Ignore exact completion */
263 if (strlen(name) > len)
264 match = strdup(name);
265 break;
269 return match;
272 static char *
273 readline_set_generator(const char *text, int state)
275 static const char *words[] = {
276 #define DEFINE_OPTION_NAME(name, type, flags) #name " = ",
277 OPTION_INFO(DEFINE_OPTION_NAME)
278 #undef DEFINE_OPTION_NAME
279 NULL
282 static int index, len;
283 const char *name;
284 char *match = NULL; /* No match */
286 /* If it is a new word to complete, initialize */
287 if (!state) {
288 index = 0;
289 len = strlen(text);
292 /* Return the next name which partially matches */
293 while ((name = words[index])) {
294 name = enum_name(name);
295 index++;
297 if (strncmp(name, text, len) == 0) {
298 /* Ignore exact completion */
299 if (strlen(name) > len)
300 match = strdup(name);
301 break;
305 return match;
308 static char *
309 readline_toggle_generator(const char *text, int state)
311 static const char **words;
312 static int index, len;
313 const char *name;
314 char *match = NULL; /* No match */
316 if (!words) {
317 /* TODO: Only complete column options that are defined
318 * for the view. */
320 #define DEFINE_OPTION_WORD(name, type, flags) argv_append(&words, #name);
321 #define DEFINE_COLUMN_OPTIONS_WORD(name, type, flags) #name,
322 #define DEFINE_COLUMN_OPTIONS_WORDS(name, id, options) \
323 if (VIEW_COLUMN_##id != VIEW_COLUMN_SECTION) { \
324 const char *vars[] = { \
325 options(DEFINE_COLUMN_OPTIONS_WORD) \
326 }; \
327 char buf[SIZEOF_STR]; \
328 int i; \
329 for (i = 0; i < ARRAY_SIZE(vars); i++) { \
330 if (enum_name_prefixed(buf, sizeof(buf), #name, vars[i])) \
331 argv_append(&words, buf); \
335 OPTION_INFO(DEFINE_OPTION_WORD)
336 COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_WORDS);
339 /* If it is a new word to complete, initialize */
340 if (!state) {
341 index = 0;
342 len = strlen(text);
345 /* Return the next name which partially matches */
346 while ((name = words[index])) {
347 name = enum_name(name);
348 index++;
350 if (strncmp(name, text, len) == 0) {
351 /* Ignore exact completion */
352 if (strlen(name) > len)
353 match = strdup(name);
354 break;
358 return match;
361 static int
362 readline_getc(FILE *stream)
364 return get_input_char();
367 static char **
368 readline_completion(const char *text, int start, int end)
370 /* Do not append a space after a completion */
371 rl_completion_suppress_append = 1;
374 * If the word is at the start of the line,
375 * then it is a tig action to complete.
377 if (start == 0)
378 return rl_completion_matches(text, readline_action_generator);
381 * If the line begins with "toggle", then we complete toggle options.
383 if (start >= 7 && strncmp(rl_line_buffer, "toggle ", 7) == 0)
384 return rl_completion_matches(text, readline_toggle_generator);
387 * If the line begins with "set", then we complete set options.
388 * (unless it is already completed)
390 if (start >= 4 && strncmp(rl_line_buffer, "set ", 4) == 0 &&
391 !strchr(rl_line_buffer, '='))
392 return rl_completion_matches(text, readline_set_generator);
395 * Otherwise it might be a variable name...
397 if (strncmp(text, "%(", 2) == 0)
398 return rl_completion_matches(text, readline_variable_generator);
401 * ... or finally fall back to filename completion.
403 return NULL;
406 static void
407 readline_display_matches(char **matches, int num_matches, int max_length)
409 unsigned int i;
411 wmove(status_win, 0, 0);
412 waddstr(status_win, "matches: ");
414 /* matches[0] is the incomplete word */
415 for (i = 1; i < num_matches + 1; ++i) {
416 waddstr(status_win, matches[i]);
417 waddch(status_win, ' ');
420 wgetch(status_win);
421 wrefresh(status_win);
424 static void
425 readline_init(void)
427 /* Allow conditional parsing of the ~/.inputrc file. */
428 rl_readline_name = "tig";
430 /* Word break caracters (we removed '(' to match variables) */
431 rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{";
433 /* Custom display function */
434 rl_redisplay_function = readline_display;
435 rl_getc_function = readline_getc;
437 /* Completion support */
438 rl_attempted_completion_function = readline_completion;
440 rl_completion_display_matches_hook = readline_display_matches;
443 char *
444 read_prompt(const char *prompt)
446 static char *line = NULL;
448 if (line) {
449 free(line);
450 line = NULL;
453 line = readline(prompt);
455 if (line && !*line) {
456 free(line);
457 line = NULL;
460 if (line)
461 add_history(line);
463 return line;
466 void
467 prompt_init(void)
469 readline_init();
471 #else
472 char *
473 read_prompt(const char *prompt)
475 return read_prompt_incremental(prompt, TRUE, NULL, NULL);
478 void
479 prompt_init(void)
482 #endif /* HAVE_READLINE */
484 bool
485 prompt_menu(const char *prompt, const struct menu_item *items, int *selected)
487 enum input_status status = INPUT_OK;
488 struct key key;
489 int size = 0;
491 while (items[size].text)
492 size++;
494 assert(size > 0);
496 while (status == INPUT_OK) {
497 const struct menu_item *item = &items[*selected];
498 char hotkey[] = { '[', (char) item->hotkey, ']', ' ', 0 };
499 int i;
501 update_status("%s (%d of %d) %s%s", prompt, *selected + 1, size,
502 item->hotkey ? hotkey : "", item->text);
504 switch (get_input(COLS - 1, &key)) {
505 case KEY_RETURN:
506 case KEY_ENTER:
507 case '\n':
508 status = INPUT_STOP;
509 break;
511 case KEY_LEFT:
512 case KEY_UP:
513 *selected = *selected - 1;
514 if (*selected < 0)
515 *selected = size - 1;
516 break;
518 case KEY_RIGHT:
519 case KEY_DOWN:
520 *selected = (*selected + 1) % size;
521 break;
523 case KEY_ESC:
524 status = INPUT_CANCEL;
525 break;
527 default:
528 for (i = 0; items[i].text; i++)
529 if (items[i].hotkey == key.data.bytes[0]) {
530 *selected = i;
531 status = INPUT_STOP;
532 break;
537 report_clear();
539 return status != INPUT_CANCEL;
542 static struct option_info option_toggles[] = {
543 #define DEFINE_OPTION_TOGGLES(name, type, flags) { #name, STRING_SIZE(#name), #type, &opt_ ## name, flags },
544 OPTION_INFO(DEFINE_OPTION_TOGGLES)
547 static bool
548 find_arg(const char *argv[], const char *arg)
550 int i;
552 for (i = 0; argv && argv[i]; i++)
553 if (!strcmp(argv[i], arg))
554 return TRUE;
555 return FALSE;
558 static enum status_code
559 prompt_toggle_option(struct view *view, const char *argv[], const char *prefix,
560 struct option_info *toggle, enum view_flag *flags)
562 char name[SIZEOF_STR];
564 if (!enum_name_prefixed(name, sizeof(name), prefix, toggle->name))
565 return error("Failed to toggle option %s", toggle->name);
567 *flags = toggle->flags;
569 if (!strcmp(toggle->type, "bool")) {
570 bool *opt = toggle->value;
572 *opt = !*opt;
573 if (opt == &opt_mouse)
574 enable_mouse(*opt);
575 return success("set %s = %s", name, *opt ? "yes" : "no");
577 } else if (!strncmp(toggle->type, "enum", 4)) {
578 const char *type = toggle->type + STRING_SIZE("enum ");
579 enum author *opt = toggle->value;
580 const struct enum_map *map = find_enum_map(type);
582 *opt = (*opt + 1) % map->size;
583 return success("set %s = %s", name, enum_name(map->entries[*opt].name));
585 } else if (!strcmp(toggle->type, "int")) {
586 const char *arg = argv[2] ? argv[2] : "1";
587 int diff = atoi(arg);
588 int *opt = toggle->value;
590 if (!diff)
591 diff = *arg == '-' ? -1 : 1;
593 if (opt == &opt_diff_context && *opt < 0)
594 *opt = -*opt;
595 if (opt == &opt_diff_context && diff < 0) {
596 if (!*opt)
597 return error("Diff context cannot be less than zero");
598 if (*opt < -diff)
599 diff = -*opt;
602 if (strstr(name, "commit-title-overflow")) {
603 *opt = *opt ? -*opt : 50;
604 if (*opt < 0)
605 return success("set %s = no", name);
606 diff = 0;
609 *opt += diff;
610 return success("set %s = %d", name, *opt);
612 } else if (!strcmp(toggle->type, "double")) {
613 const char *arg = argv[2] ? argv[2] : "1.0";
614 double *opt = toggle->value;
615 int sign = 1;
616 double diff;
618 if (*arg == '-') {
619 sign = -1;
620 arg++;
623 if (parse_step(&diff, arg) != SUCCESS)
624 diff = strtod(arg, NULL);
626 *opt += sign * diff;
627 return success("set %s = %.2f", name, *opt);
629 } else if (!strcmp(toggle->type, "const char **")) {
630 const char ***opt = toggle->value;
631 bool found = TRUE;
632 int i;
634 if (argv_size(argv) <= 2) {
635 argv_free(*opt);
636 (*opt)[0] = NULL;
637 return SUCCESS;
640 for (i = 2; argv[i]; i++) {
641 if (!find_arg(*opt, argv[i])) {
642 found = FALSE;
643 break;
647 if (found) {
648 int next, pos;
650 for (next = 0, pos = 0; (*opt)[pos]; pos++) {
651 const char *arg = (*opt)[pos];
653 if (find_arg(argv + 2, arg)) {
654 free((void *) arg);
655 continue;
657 (*opt)[next++] = arg;
660 (*opt)[next] = NULL;
662 } else if (!argv_copy(opt, argv + 2)) {
663 return ERROR_OUT_OF_MEMORY;
665 return SUCCESS;
667 } else {
668 return error("Unsupported `:toggle %s` (%s)", name, toggle->type);
672 static enum status_code
673 prompt_toggle(struct view *view, const char *argv[], enum view_flag *flags)
675 const char *option = argv[1];
676 size_t optionlen = option ? strlen(option) : 0;
677 struct option_info template;
678 struct option_info *toggle;
679 struct view_column *column;
680 const char *column_name;
682 if (!option)
683 return error("%s", "No option name given to :toggle");
685 if (enum_equals_static("sort-field", option, optionlen) ||
686 enum_equals_static("sort-order", option, optionlen)) {
687 if (!view_has_flags(view, VIEW_SORTABLE)) {
688 return error("Sorting is not yet supported for the %s view", view->name);
689 } else {
690 bool sort_field = enum_equals_static("sort-field", option, optionlen);
691 struct sort_state *sort = &view->sort;
693 sort_view(view, sort_field);
694 return success("set %s = %s", option,
695 sort_field ? view_column_name(get_sort_field(view))
696 : sort->reverse ? "descending" : "ascending");
700 toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles), "", option);
701 if (toggle)
702 return prompt_toggle_option(view, argv, "", toggle, flags);
704 for (column = view->columns; column; column = column->next) {
705 toggle = find_column_option_info(column->type, &column->opt, option, &template, &column_name);
706 if (toggle)
707 return prompt_toggle_option(view, argv, column_name, toggle, flags);
710 return error("`:toggle %s` not supported", option);
713 static void
714 prompt_update_display(enum view_flag flags)
716 struct view *view;
717 int i;
719 if (flags & VIEW_RESET_DISPLAY) {
720 resize_display();
721 redraw_display(TRUE);
724 foreach_displayed_view(view, i) {
725 if (view_has_flags(view, flags) && view_can_refresh(view))
726 reload_view(view);
727 else
728 redraw_view(view);
732 enum request
733 run_prompt_command(struct view *view, const char *argv[])
735 enum request request;
736 const char *cmd = argv[0];
737 size_t cmdlen = cmd ? strlen(cmd) : 0;
739 if (!cmd)
740 return REQ_NONE;
742 if (string_isnumber(cmd)) {
743 int lineno = view->pos.lineno + 1;
745 if (parse_int(&lineno, cmd, 0, view->lines + 1) == SUCCESS) {
746 if (!lineno)
747 lineno = 1;
748 select_view_line(view, lineno - 1);
749 report_clear();
750 } else {
751 report("Unable to parse '%s' as a line number", cmd);
753 } else if (iscommit(cmd)) {
754 int lineno;
756 if (!(view->ops->column_bits & view_column_bit(ID))) {
757 report("Jumping to commits is not supported by the %s view", view->name);
758 return REQ_NONE;
761 for (lineno = 0; lineno < view->lines; lineno++) {
762 struct view_column_data column_data = {0};
763 struct line *line = &view->line[lineno];
765 if (view->ops->get_column_data(view, line, &column_data) &&
766 column_data.id &&
767 !strncasecmp(column_data.id, cmd, cmdlen)) {
768 string_ncopy(view->env->search, cmd, cmdlen);
769 select_view_line(view, lineno);
770 report_clear();
771 return REQ_NONE;
775 report("Unable to find commit '%s'", view->env->search);
776 return REQ_NONE;
778 } else if (cmdlen > 1 && (cmd[0] == '/' || cmd[0] == '?')) {
779 char search[SIZEOF_STR];
781 if (!argv_to_string(argv, search, sizeof(search), " ")) {
782 report("Failed to copy search string");
783 return REQ_NONE;
786 if (!strcmp(search + 1, view->env->search))
787 return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV;
789 string_ncopy(view->env->search, search + 1, strlen(search + 1));
790 return cmd[0] == '/' ? REQ_SEARCH : REQ_SEARCH_BACK;
792 } else if (cmdlen > 1 && cmd[0] == '!') {
793 struct view *next = &pager_view;
794 bool copied;
796 /* Trim the leading '!'. */
797 argv[0] = cmd + 1;
798 copied = argv_format(view->env, &next->argv, argv, FALSE, TRUE);
799 argv[0] = cmd;
801 if (!copied) {
802 report("Argument formatting failed");
803 } else {
804 /* When running random commands, initially show the
805 * command in the title. However, it maybe later be
806 * overwritten if a commit line is selected. */
807 argv_to_string(next->argv, next->ref, sizeof(next->ref), " ");
809 next->dir = NULL;
810 open_pager_view(view, OPEN_PREPARED | OPEN_WITH_STDERR);
813 } else if (!strcmp(cmd, "save-display")) {
814 const char *path = argv[1] ? argv[1] : "tig-display.txt";
816 if (!save_display(path))
817 report("Failed to save screen to %s", path);
818 else
819 report("Saved screen to %s", path);
821 } else if (!strcmp(cmd, "save-options")) {
822 const char *path = argv[1] ? argv[1] : "tig-options.txt";
823 enum status_code code = save_options(path);
825 if (code != SUCCESS)
826 report("Failed to save options: %s", get_status_message(code));
827 else
828 report("Saved options to %s", path);
830 } else if (!strcmp(cmd, "exec")) {
831 struct run_request req = { view->keymap, {0}, argv + 1 };
832 enum status_code code = parse_run_request_flags(&req.flags, argv + 1);
834 if (code != SUCCESS) {
835 report("Failed to execute command: %s", get_status_message(code));
836 } else {
837 return exec_run_request(view, &req);
840 } else if (!strcmp(cmd, "toggle")) {
841 enum view_flag flags = VIEW_NO_FLAGS;
842 enum status_code code = prompt_toggle(view, argv, &flags);
843 const char *action = get_status_message(code);
845 if (code != SUCCESS) {
846 report("%s", action);
847 return REQ_NONE;
850 prompt_update_display(flags);
852 if (*action)
853 report("%s", action);
855 } else if (!strcmp(cmd, "script")) {
856 enum status_code code = open_script(argv[1]);
858 if (code != SUCCESS)
859 report("%s", get_status_message(code));
860 return REQ_NONE;
862 } else {
863 struct key key = {{0}};
864 enum status_code code;
865 enum view_flag flags = VIEW_NO_FLAGS;
867 /* Try :<key> */
868 key.modifiers.multibytes = 1;
869 string_ncopy(key.data.bytes, cmd, cmdlen);
870 request = get_keybinding(view->keymap, &key, 1, NULL);
871 if (request != REQ_UNKNOWN)
872 return request;
874 /* Try :<command> */
875 request = get_request(cmd);
876 if (request != REQ_UNKNOWN)
877 return request;
879 code = set_option(argv[0], argv_size(argv + 1), &argv[1]);
880 if (code != SUCCESS) {
881 report("%s", get_status_message(code));
882 return REQ_NONE;
885 if (!strcmp(cmd, "set")) {
886 struct option_info *toggle;
888 toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles),
889 "", argv[1]);
891 if (toggle)
892 flags = toggle->flags;
895 if (flags) {
896 prompt_update_display(flags);
898 } else {
899 request = view_can_refresh(view) ? REQ_REFRESH : REQ_SCREEN_REDRAW;
900 if (!strcmp(cmd, "color"))
901 init_colors();
902 resize_display();
903 redraw_display(TRUE);
907 return REQ_NONE;
910 enum request
911 exec_run_request(struct view *view, struct run_request *req)
913 const char **argv = NULL;
914 bool confirmed = FALSE;
915 enum request request = REQ_NONE;
916 char cmd[SIZEOF_STR];
917 const char *req_argv[SIZEOF_ARG];
918 int req_argc = 0;
920 if (!argv_to_string(req->argv, cmd, sizeof(cmd), " ")
921 || !argv_from_string_no_quotes(req_argv, &req_argc, cmd)
922 || !argv_format(view->env, &argv, req_argv, FALSE, TRUE)) {
923 report("Failed to format arguments");
924 return REQ_NONE;
927 if (req->flags.internal) {
928 request = run_prompt_command(view, argv);
930 } else {
931 confirmed = !req->flags.confirm;
933 if (req->flags.confirm) {
934 char cmd[SIZEOF_STR], prompt[SIZEOF_STR];
935 const char *and_exit = req->flags.exit ? " and exit" : "";
937 if (argv_to_string_quoted(argv, cmd, sizeof(cmd), " ") &&
938 string_format(prompt, "Run `%s`%s?", cmd, and_exit) &&
939 prompt_yesno(prompt)) {
940 confirmed = TRUE;
944 if (confirmed)
945 open_external_viewer(argv, NULL, req->flags.silent,
946 !req->flags.exit, FALSE, "");
949 if (argv)
950 argv_free(argv);
951 free(argv);
953 if (request == REQ_NONE) {
954 if (req->flags.confirm && !confirmed)
955 request = REQ_NONE;
957 else if (req->flags.exit)
958 request = REQ_QUIT;
960 else if (!req->flags.internal && watch_dirty(&view->watch))
961 request = REQ_REFRESH;
965 return request;
968 enum request
969 open_prompt(struct view *view)
971 char *cmd = read_prompt(":");
972 const char *argv[SIZEOF_ARG] = { NULL };
973 int argc = 0;
975 if (cmd && !argv_from_string(argv, &argc, cmd)) {
976 report("Too many arguments");
977 return REQ_NONE;
980 return run_prompt_command(view, argv);
983 /* vim: set ts=8 sw=8 noexpandtab: */