prompt: teach read_prompt_incremental() to allow empty input
[tig.git] / src / prompt.c
blob9424b42a102a8c09e401e00906dcdb1a6b4861a1
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->allow_empty ? 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, FALSE, 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, bool allow_empty, input_handler handler, void *data)
170 static struct incremental_input incremental = { { read_prompt_handler } };
172 incremental.input.allow_empty = allow_empty;
173 incremental.input.data = data;
174 incremental.handler = handler;
175 incremental.edit_mode = edit_mode;
177 return prompt_input(prompt, (struct input *) &incremental);
180 #ifdef HAVE_READLINE
181 static void
182 readline_display(void)
184 update_status("%s%s", rl_display_prompt, rl_line_buffer);
185 wmove(status_win, 0, strlen(rl_display_prompt) + rl_point);
186 wrefresh(status_win);
189 static char *
190 readline_variable_generator(const char *text, int state)
192 static const char *vars[] = {
193 #define FORMAT_VAR(type, name, ifempty, initval) "%(" #name ")",
194 ARGV_ENV_INFO(FORMAT_VAR)
195 #undef FORMAT_VAR
196 NULL
199 static int index, len;
200 const char *name;
201 char *variable = NULL; /* No match */
203 /* If it is a new word to complete, initialize */
204 if (!state) {
205 index = 0;
206 len = strlen(text);
209 /* Return the next name which partially matches */
210 while ((name = vars[index])) {
211 index++;
213 /* Complete or format a variable */
214 if (strncmp(name, text, len) == 0) {
215 if (strlen(name) > len)
216 variable = strdup(name);
217 else
218 variable = argv_format_arg(&argv_env, text);
219 break;
223 return variable;
226 static char *
227 readline_action_generator(const char *text, int state)
229 static const char *actions[] = {
230 "!",
231 "source",
232 "color",
233 "bind",
234 "set",
235 "toggle",
236 "save-display",
237 "save-options",
238 "exec",
239 #define REQ_GROUP(help)
240 #define REQ_(req, help) #req
241 REQ_INFO,
242 #undef REQ_GROUP
243 #undef REQ_
244 NULL
247 static int index, len;
248 const char *name;
249 char *match = NULL; /* No match */
251 /* If it is a new word to complete, initialize */
252 if (!state) {
253 index = 0;
254 len = strlen(text);
257 /* Return the next name which partially matches */
258 while ((name = actions[index])) {
259 name = enum_name(name);
260 index++;
262 if (strncmp(name, text, len) == 0) {
263 /* Ignore exact completion */
264 if (strlen(name) > len)
265 match = strdup(name);
266 break;
270 return match;
273 static char *
274 readline_set_generator(const char *text, int state)
276 static const char *words[] = {
277 #define DEFINE_OPTION_NAME(name, type, flags) #name " = ",
278 OPTION_INFO(DEFINE_OPTION_NAME)
279 #undef DEFINE_OPTION_NAME
280 NULL
283 static int index, len;
284 const char *name;
285 char *match = NULL; /* No match */
287 /* If it is a new word to complete, initialize */
288 if (!state) {
289 index = 0;
290 len = strlen(text);
293 /* Return the next name which partially matches */
294 while ((name = words[index])) {
295 name = enum_name(name);
296 index++;
298 if (strncmp(name, text, len) == 0) {
299 /* Ignore exact completion */
300 if (strlen(name) > len)
301 match = strdup(name);
302 break;
306 return match;
309 static char *
310 readline_toggle_generator(const char *text, int state)
312 static const char **words;
313 static int index, len;
314 const char *name;
315 char *match = NULL; /* No match */
317 if (!words) {
318 /* TODO: Only complete column options that are defined
319 * for the view. */
321 #define DEFINE_OPTION_WORD(name, type, flags) argv_append(&words, #name);
322 #define DEFINE_COLUMN_OPTIONS_WORD(name, type, flags) #name,
323 #define DEFINE_COLUMN_OPTIONS_WORDS(name, id, options) \
324 if (VIEW_COLUMN_##id != VIEW_COLUMN_SECTION) { \
325 const char *vars[] = { \
326 options(DEFINE_COLUMN_OPTIONS_WORD) \
327 }; \
328 char buf[SIZEOF_STR]; \
329 int i; \
330 for (i = 0; i < ARRAY_SIZE(vars); i++) { \
331 if (enum_name_prefixed(buf, sizeof(buf), #name, vars[i])) \
332 argv_append(&words, buf); \
336 OPTION_INFO(DEFINE_OPTION_WORD)
337 COLUMN_OPTIONS(DEFINE_COLUMN_OPTIONS_WORDS);
340 /* If it is a new word to complete, initialize */
341 if (!state) {
342 index = 0;
343 len = strlen(text);
346 /* Return the next name which partially matches */
347 while ((name = words[index])) {
348 name = enum_name(name);
349 index++;
351 if (strncmp(name, text, len) == 0) {
352 /* Ignore exact completion */
353 if (strlen(name) > len)
354 match = strdup(name);
355 break;
359 return match;
362 static int
363 readline_getc(FILE *stream)
365 return get_input_char();
368 static char **
369 readline_completion(const char *text, int start, int end)
371 /* Do not append a space after a completion */
372 rl_completion_suppress_append = 1;
375 * If the word is at the start of the line,
376 * then it is a tig action to complete.
378 if (start == 0)
379 return rl_completion_matches(text, readline_action_generator);
382 * If the line begins with "toggle", then we complete toggle options.
384 if (start >= 7 && strncmp(rl_line_buffer, "toggle ", 7) == 0)
385 return rl_completion_matches(text, readline_toggle_generator);
388 * If the line begins with "set", then we complete set options.
389 * (unless it is already completed)
391 if (start >= 4 && strncmp(rl_line_buffer, "set ", 4) == 0 &&
392 !strchr(rl_line_buffer, '='))
393 return rl_completion_matches(text, readline_set_generator);
396 * Otherwise it might be a variable name...
398 if (strncmp(text, "%(", 2) == 0)
399 return rl_completion_matches(text, readline_variable_generator);
402 * ... or finally fall back to filename completion.
404 return NULL;
407 static void
408 readline_display_matches(char **matches, int num_matches, int max_length)
410 unsigned int i;
412 wmove(status_win, 0, 0);
413 waddstr(status_win, "matches: ");
415 /* matches[0] is the incomplete word */
416 for (i = 1; i < num_matches + 1; ++i) {
417 waddstr(status_win, matches[i]);
418 waddch(status_win, ' ');
421 wgetch(status_win);
422 wrefresh(status_win);
425 static void
426 readline_init(void)
428 /* Allow conditional parsing of the ~/.inputrc file. */
429 rl_readline_name = "tig";
431 /* Word break caracters (we removed '(' to match variables) */
432 rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{";
434 /* Custom display function */
435 rl_redisplay_function = readline_display;
436 rl_getc_function = readline_getc;
438 /* Completion support */
439 rl_attempted_completion_function = readline_completion;
441 rl_completion_display_matches_hook = readline_display_matches;
444 char *
445 read_prompt(const char *prompt)
447 static char *line = NULL;
449 if (line) {
450 free(line);
451 line = NULL;
454 line = readline(prompt);
456 if (line && !*line) {
457 free(line);
458 line = NULL;
461 if (line)
462 add_history(line);
464 return line;
467 void
468 prompt_init(void)
470 readline_init();
472 #else
473 char *
474 read_prompt(const char *prompt)
476 return read_prompt_incremental(prompt, TRUE, FALSE, NULL, NULL);
479 void
480 prompt_init(void)
483 #endif /* HAVE_READLINE */
485 bool
486 prompt_menu(const char *prompt, const struct menu_item *items, int *selected)
488 enum input_status status = INPUT_OK;
489 struct key key;
490 int size = 0;
492 while (items[size].text)
493 size++;
495 assert(size > 0);
497 while (status == INPUT_OK) {
498 const struct menu_item *item = &items[*selected];
499 char hotkey[] = { '[', (char) item->hotkey, ']', ' ', 0 };
500 int i;
502 update_status("%s (%d of %d) %s%s", prompt, *selected + 1, size,
503 item->hotkey ? hotkey : "", item->text);
505 switch (get_input(COLS - 1, &key)) {
506 case KEY_RETURN:
507 case KEY_ENTER:
508 case '\n':
509 status = INPUT_STOP;
510 break;
512 case KEY_LEFT:
513 case KEY_UP:
514 *selected = *selected - 1;
515 if (*selected < 0)
516 *selected = size - 1;
517 break;
519 case KEY_RIGHT:
520 case KEY_DOWN:
521 *selected = (*selected + 1) % size;
522 break;
524 case KEY_ESC:
525 status = INPUT_CANCEL;
526 break;
528 default:
529 for (i = 0; items[i].text; i++)
530 if (items[i].hotkey == key.data.bytes[0]) {
531 *selected = i;
532 status = INPUT_STOP;
533 break;
538 report_clear();
540 return status != INPUT_CANCEL;
543 static struct option_info option_toggles[] = {
544 #define DEFINE_OPTION_TOGGLES(name, type, flags) { #name, STRING_SIZE(#name), #type, &opt_ ## name, flags },
545 OPTION_INFO(DEFINE_OPTION_TOGGLES)
548 static bool
549 find_arg(const char *argv[], const char *arg)
551 int i;
553 for (i = 0; argv && argv[i]; i++)
554 if (!strcmp(argv[i], arg))
555 return TRUE;
556 return FALSE;
559 static enum status_code
560 prompt_toggle_option(struct view *view, const char *argv[], const char *prefix,
561 struct option_info *toggle, enum view_flag *flags)
563 char name[SIZEOF_STR];
565 if (!enum_name_prefixed(name, sizeof(name), prefix, toggle->name))
566 return error("Failed to toggle option %s", toggle->name);
568 *flags = toggle->flags;
570 if (!strcmp(toggle->type, "bool")) {
571 bool *opt = toggle->value;
573 *opt = !*opt;
574 if (opt == &opt_mouse)
575 enable_mouse(*opt);
576 return success("set %s = %s", name, *opt ? "yes" : "no");
578 } else if (!strncmp(toggle->type, "enum", 4)) {
579 const char *type = toggle->type + STRING_SIZE("enum ");
580 enum author *opt = toggle->value;
581 const struct enum_map *map = find_enum_map(type);
583 *opt = (*opt + 1) % map->size;
584 return success("set %s = %s", name, enum_name(map->entries[*opt].name));
586 } else if (!strcmp(toggle->type, "int")) {
587 const char *arg = argv[2] ? argv[2] : "1";
588 int diff = atoi(arg);
589 int *opt = toggle->value;
591 if (!diff)
592 diff = *arg == '-' ? -1 : 1;
594 if (opt == &opt_diff_context && *opt < 0)
595 *opt = -*opt;
596 if (opt == &opt_diff_context && diff < 0) {
597 if (!*opt)
598 return error("Diff context cannot be less than zero");
599 if (*opt < -diff)
600 diff = -*opt;
603 if (strstr(name, "commit-title-overflow")) {
604 *opt = *opt ? -*opt : 50;
605 if (*opt < 0)
606 return success("set %s = no", name);
607 diff = 0;
610 *opt += diff;
611 return success("set %s = %d", name, *opt);
613 } else if (!strcmp(toggle->type, "double")) {
614 const char *arg = argv[2] ? argv[2] : "1.0";
615 double *opt = toggle->value;
616 int sign = 1;
617 double diff;
619 if (*arg == '-') {
620 sign = -1;
621 arg++;
624 if (parse_step(&diff, arg) != SUCCESS)
625 diff = strtod(arg, NULL);
627 *opt += sign * diff;
628 return success("set %s = %.2f", name, *opt);
630 } else if (!strcmp(toggle->type, "const char **")) {
631 const char ***opt = toggle->value;
632 bool found = TRUE;
633 int i;
635 if (argv_size(argv) <= 2) {
636 argv_free(*opt);
637 (*opt)[0] = NULL;
638 return SUCCESS;
641 for (i = 2; argv[i]; i++) {
642 if (!find_arg(*opt, argv[i])) {
643 found = FALSE;
644 break;
648 if (found) {
649 int next, pos;
651 for (next = 0, pos = 0; (*opt)[pos]; pos++) {
652 const char *arg = (*opt)[pos];
654 if (find_arg(argv + 2, arg)) {
655 free((void *) arg);
656 continue;
658 (*opt)[next++] = arg;
661 (*opt)[next] = NULL;
663 } else if (!argv_copy(opt, argv + 2)) {
664 return ERROR_OUT_OF_MEMORY;
666 return SUCCESS;
668 } else {
669 return error("Unsupported `:toggle %s` (%s)", name, toggle->type);
673 static enum status_code
674 prompt_toggle(struct view *view, const char *argv[], enum view_flag *flags)
676 const char *option = argv[1];
677 size_t optionlen = option ? strlen(option) : 0;
678 struct option_info template;
679 struct option_info *toggle;
680 struct view_column *column;
681 const char *column_name;
683 if (!option)
684 return error("%s", "No option name given to :toggle");
686 if (enum_equals_static("sort-field", option, optionlen) ||
687 enum_equals_static("sort-order", option, optionlen)) {
688 if (!view_has_flags(view, VIEW_SORTABLE)) {
689 return error("Sorting is not yet supported for the %s view", view->name);
690 } else {
691 bool sort_field = enum_equals_static("sort-field", option, optionlen);
692 struct sort_state *sort = &view->sort;
694 sort_view(view, sort_field);
695 return success("set %s = %s", option,
696 sort_field ? view_column_name(get_sort_field(view))
697 : sort->reverse ? "descending" : "ascending");
701 toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles), "", option);
702 if (toggle)
703 return prompt_toggle_option(view, argv, "", toggle, flags);
705 for (column = view->columns; column; column = column->next) {
706 toggle = find_column_option_info(column->type, &column->opt, option, &template, &column_name);
707 if (toggle)
708 return prompt_toggle_option(view, argv, column_name, toggle, flags);
711 return error("`:toggle %s` not supported", option);
714 static void
715 prompt_update_display(enum view_flag flags)
717 struct view *view;
718 int i;
720 if (flags & VIEW_RESET_DISPLAY) {
721 resize_display();
722 redraw_display(TRUE);
725 foreach_displayed_view(view, i) {
726 if (view_has_flags(view, flags) && view_can_refresh(view))
727 reload_view(view);
728 else
729 redraw_view(view);
733 enum request
734 run_prompt_command(struct view *view, const char *argv[])
736 enum request request;
737 const char *cmd = argv[0];
738 size_t cmdlen = cmd ? strlen(cmd) : 0;
740 if (!cmd)
741 return REQ_NONE;
743 if (string_isnumber(cmd)) {
744 int lineno = view->pos.lineno + 1;
746 if (parse_int(&lineno, cmd, 0, view->lines + 1) == SUCCESS) {
747 if (!lineno)
748 lineno = 1;
749 select_view_line(view, lineno - 1);
750 report_clear();
751 } else {
752 report("Unable to parse '%s' as a line number", cmd);
754 } else if (iscommit(cmd)) {
755 int lineno;
757 if (!(view->ops->column_bits & view_column_bit(ID))) {
758 report("Jumping to commits is not supported by the %s view", view->name);
759 return REQ_NONE;
762 for (lineno = 0; lineno < view->lines; lineno++) {
763 struct view_column_data column_data = {0};
764 struct line *line = &view->line[lineno];
766 if (view->ops->get_column_data(view, line, &column_data) &&
767 column_data.id &&
768 !strncasecmp(column_data.id, cmd, cmdlen)) {
769 string_ncopy(view->env->search, cmd, cmdlen);
770 select_view_line(view, lineno);
771 report_clear();
772 return REQ_NONE;
776 report("Unable to find commit '%s'", view->env->search);
777 return REQ_NONE;
779 } else if (cmdlen > 1 && (cmd[0] == '/' || cmd[0] == '?')) {
780 char search[SIZEOF_STR];
782 if (!argv_to_string(argv, search, sizeof(search), " ")) {
783 report("Failed to copy search string");
784 return REQ_NONE;
787 if (!strcmp(search + 1, view->env->search))
788 return cmd[0] == '/' ? REQ_FIND_NEXT : REQ_FIND_PREV;
790 string_ncopy(view->env->search, search + 1, strlen(search + 1));
791 return cmd[0] == '/' ? REQ_SEARCH : REQ_SEARCH_BACK;
793 } else if (cmdlen > 1 && cmd[0] == '!') {
794 struct view *next = &pager_view;
795 bool copied;
797 /* Trim the leading '!'. */
798 argv[0] = cmd + 1;
799 copied = argv_format(view->env, &next->argv, argv, FALSE, TRUE);
800 argv[0] = cmd;
802 if (!copied) {
803 report("Argument formatting failed");
804 } else {
805 /* When running random commands, initially show the
806 * command in the title. However, it maybe later be
807 * overwritten if a commit line is selected. */
808 argv_to_string(next->argv, next->ref, sizeof(next->ref), " ");
810 next->dir = NULL;
811 open_pager_view(view, OPEN_PREPARED | OPEN_WITH_STDERR);
814 } else if (!strcmp(cmd, "save-display")) {
815 const char *path = argv[1] ? argv[1] : "tig-display.txt";
817 if (!save_display(path))
818 report("Failed to save screen to %s", path);
819 else
820 report("Saved screen to %s", path);
822 } else if (!strcmp(cmd, "save-options")) {
823 const char *path = argv[1] ? argv[1] : "tig-options.txt";
824 enum status_code code = save_options(path);
826 if (code != SUCCESS)
827 report("Failed to save options: %s", get_status_message(code));
828 else
829 report("Saved options to %s", path);
831 } else if (!strcmp(cmd, "exec")) {
832 struct run_request req = { view->keymap, {0}, argv + 1 };
833 enum status_code code = parse_run_request_flags(&req.flags, argv + 1);
835 if (code != SUCCESS) {
836 report("Failed to execute command: %s", get_status_message(code));
837 } else {
838 return exec_run_request(view, &req);
841 } else if (!strcmp(cmd, "toggle")) {
842 enum view_flag flags = VIEW_NO_FLAGS;
843 enum status_code code = prompt_toggle(view, argv, &flags);
844 const char *action = get_status_message(code);
846 if (code != SUCCESS) {
847 report("%s", action);
848 return REQ_NONE;
851 prompt_update_display(flags);
853 if (*action)
854 report("%s", action);
856 } else if (!strcmp(cmd, "script")) {
857 enum status_code code = open_script(argv[1]);
859 if (code != SUCCESS)
860 report("%s", get_status_message(code));
861 return REQ_NONE;
863 } else {
864 struct key key = {{0}};
865 enum status_code code;
866 enum view_flag flags = VIEW_NO_FLAGS;
868 /* Try :<key> */
869 key.modifiers.multibytes = 1;
870 string_ncopy(key.data.bytes, cmd, cmdlen);
871 request = get_keybinding(view->keymap, &key, 1, NULL);
872 if (request != REQ_UNKNOWN)
873 return request;
875 /* Try :<command> */
876 request = get_request(cmd);
877 if (request != REQ_UNKNOWN)
878 return request;
880 code = set_option(argv[0], argv_size(argv + 1), &argv[1]);
881 if (code != SUCCESS) {
882 report("%s", get_status_message(code));
883 return REQ_NONE;
886 if (!strcmp(cmd, "set")) {
887 struct option_info *toggle;
889 toggle = find_option_info(option_toggles, ARRAY_SIZE(option_toggles),
890 "", argv[1]);
892 if (toggle)
893 flags = toggle->flags;
896 if (flags) {
897 prompt_update_display(flags);
899 } else {
900 request = view_can_refresh(view) ? REQ_REFRESH : REQ_SCREEN_REDRAW;
901 if (!strcmp(cmd, "color"))
902 init_colors();
903 resize_display();
904 redraw_display(TRUE);
908 return REQ_NONE;
911 enum request
912 exec_run_request(struct view *view, struct run_request *req)
914 const char **argv = NULL;
915 bool confirmed = FALSE;
916 enum request request = REQ_NONE;
917 char cmd[SIZEOF_STR];
918 const char *req_argv[SIZEOF_ARG];
919 int req_argc = 0;
921 if (!argv_to_string(req->argv, cmd, sizeof(cmd), " ")
922 || !argv_from_string_no_quotes(req_argv, &req_argc, cmd)
923 || !argv_format(view->env, &argv, req_argv, FALSE, TRUE)) {
924 report("Failed to format arguments");
925 return REQ_NONE;
928 if (req->flags.internal) {
929 request = run_prompt_command(view, argv);
931 } else {
932 confirmed = !req->flags.confirm;
934 if (req->flags.confirm) {
935 char cmd[SIZEOF_STR], prompt[SIZEOF_STR];
936 const char *and_exit = req->flags.exit ? " and exit" : "";
938 if (argv_to_string_quoted(argv, cmd, sizeof(cmd), " ") &&
939 string_format(prompt, "Run `%s`%s?", cmd, and_exit) &&
940 prompt_yesno(prompt)) {
941 confirmed = TRUE;
945 if (confirmed)
946 open_external_viewer(argv, NULL, req->flags.silent,
947 !req->flags.exit, FALSE, "");
950 if (argv)
951 argv_free(argv);
952 free(argv);
954 if (request == REQ_NONE) {
955 if (req->flags.confirm && !confirmed)
956 request = REQ_NONE;
958 else if (req->flags.exit)
959 request = REQ_QUIT;
961 else if (!req->flags.internal && watch_dirty(&view->watch))
962 request = REQ_REFRESH;
966 return request;
969 enum request
970 open_prompt(struct view *view)
972 char *cmd = read_prompt(":");
973 const char *argv[SIZEOF_ARG] = { NULL };
974 int argc = 0;
976 if (cmd && !argv_from_string(argv, &argc, cmd)) {
977 report("Too many arguments");
978 return REQ_NONE;
981 return run_prompt_command(view, argv);
984 /* vim: set ts=8 sw=8 noexpandtab: */