}
struct list_options {
+ int columns;
const char *header;
void (*print_item)(int i, struct string_list_item *item, void *print_item_data);
void *print_item_data;
static void list(struct add_i_state *s, struct string_list *list,
struct list_options *opts)
{
- int i;
+ int i, last_lf = 0;
if (!list->nr)
return;
for (i = 0; i < list->nr; i++) {
opts->print_item(i, list->items + i, opts->print_item_data);
+
+ if ((opts->columns) && ((i + 1) % (opts->columns))) {
+ putchar('\t');
+ last_lf = 0;
+ }
+ else {
+ putchar('\n');
+ last_lf = 1;
+ }
+ }
+
+ if (!last_lf)
putchar('\n');
+}
+struct list_and_choose_options {
+ struct list_options list_opts;
+
+ const char *prompt;
+};
+
+#define LIST_AND_CHOOSE_ERROR (-1)
+#define LIST_AND_CHOOSE_QUIT (-2)
+
+/*
+ * Returns the selected index.
+ *
+ * If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
+ * `LIST_AND_CHOOSE_QUIT` is returned.
+ */
+static ssize_t list_and_choose(struct add_i_state *s, struct string_list *items,
+ struct list_and_choose_options *opts)
+{
+ struct strbuf input = STRBUF_INIT;
+ ssize_t res = LIST_AND_CHOOSE_ERROR;
+
+ for (;;) {
+ char *p;
+
+ strbuf_reset(&input);
+
+ list(s, items, &opts->list_opts);
+
+ printf("%s%s", opts->prompt, "> ");
+ fflush(stdout);
+
+ if (strbuf_getline(&input, stdin) == EOF) {
+ putchar('\n');
+ res = LIST_AND_CHOOSE_QUIT;
+ break;
+ }
+ strbuf_trim(&input);
+
+ if (!input.len)
+ break;
+
+ p = input.buf;
+ for (;;) {
+ size_t sep = strcspn(p, " \t\r\n,");
+ ssize_t index = -1;
+
+ if (!sep) {
+ if (!*p)
+ break;
+ p++;
+ continue;
+ }
+
+ if (isdigit(*p)) {
+ char *endp;
+ index = strtoul(p, &endp, 10) - 1;
+ if (endp != p + sep)
+ index = -1;
+ }
+
+ if (p[sep])
+ p[sep++] = '\0';
+ if (index < 0 || index >= items->nr)
+ printf(_("Huh (%s)?\n"), p);
+ else {
+ res = index;
+ break;
+ }
+
+ p += sep;
+ }
+
+ if (res != LIST_AND_CHOOSE_ERROR)
+ break;
}
+
+ strbuf_release(&input);
+ return res;
}
struct adddel {
return 0;
}
+typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
+ struct string_list *files,
+ struct list_options *opts);
+
+static void print_command_item(int i, struct string_list_item *item,
+ void *print_command_item_data)
+{
+ printf(" %2d: %s", i + 1, item->string);
+}
+
int run_add_i(struct repository *r, const struct pathspec *ps)
{
struct add_i_state s = { NULL };
+ struct list_and_choose_options main_loop_opts = {
+ { 4, N_("*** Commands ***"), print_command_item, NULL },
+ N_("What now")
+ };
+ struct {
+ const char *string;
+ command_t command;
+ } command_list[] = {
+ { "status", run_status },
+ };
+ struct string_list commands = STRING_LIST_INIT_NODUP;
+
struct print_file_item_data print_file_item_data = {
"%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
};
struct list_options opts = {
- NULL, print_file_item, &print_file_item_data
+ 0, NULL, print_file_item, &print_file_item_data
};
struct strbuf header = STRBUF_INIT;
struct string_list files = STRING_LIST_INIT_DUP;
+ ssize_t i;
int res = 0;
+ for (i = 0; i < ARRAY_SIZE(command_list); i++)
+ string_list_append(&commands, command_list[i].string)
+ ->util = command_list[i].command;
+
init_add_i_state(&s, r);
+
strbuf_addstr(&header, " ");
strbuf_addf(&header, print_file_item_data.modified_fmt,
_("staged"), _("unstaged"), _("path"));
res = run_status(&s, ps, &files, &opts);
+ for (;;) {
+ i = list_and_choose(&s, &commands, &main_loop_opts);
+ if (i == LIST_AND_CHOOSE_QUIT) {
+ printf(_("Bye.\n"));
+ res = 0;
+ break;
+ }
+ if (i != LIST_AND_CHOOSE_ERROR) {
+ command_t command = commands.items[i].util;
+ res = command(&s, ps, &files, &opts);
+ }
+ }
+
string_list_clear(&files, 1);
strbuf_release(&print_file_item_data.buf);
strbuf_release(&print_file_item_data.index);
strbuf_release(&print_file_item_data.worktree);
strbuf_release(&header);
+ string_list_clear(&commands, 0);
return res;
}