Skip to content

Commit 6348bfb

Browse files
dschogitster
authored andcommitted
built-in add -i: implement the main loop
The reason why we did not start with the main loop to begin with is that it is the first user of `list_and_choose()`, which uses the `list()` function that we conveniently introduced for use by the `status` command. In contrast to the Perl version, in the built-in interactive `add`, we will keep the `list()` function (which only displays items) and the `list_and_choose()` function (which uses `list()` to display the items, and only takes care of the "and choose" part) separate. The `list_and_choose()` function, as implemented in `git-add--interactive.perl` knows a few more tricks than the function we introduce in this patch: - There is a flag to let the user select multiple items. - In multi-select mode, the list of items is prefixed with a marker indicating what items have been selected. - Initially, for each item a unique prefix is determined (if there exists any within the given parameters), and shown in the list, and accepted as a shortcut for the selection. These features will be implemented in the C version later. This patch does not add any new main loop command, of course, the built-in `git add -i` still only supports the `status` command. The remaining commands to follow over the course of the next commits. To accommodate for listing the commands in columns, preparing for the commands that will be implemented over the course of the next patches/patch series, we teach the `list()` function to do precisely that. Note that we only have a prompt ending in a single ">" at this stage; later commits will add commands that display a double ">>" to indicate that the user is in a different loop than the main one. Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1daaebc commit 6348bfb

File tree

1 file changed

+135
-2
lines changed

1 file changed

+135
-2
lines changed

add-interactive.c

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ static void init_add_i_state(struct add_i_state *s, struct repository *r)
4646
}
4747

4848
struct list_options {
49+
int columns;
4950
const char *header;
5051
void (*print_item)(int i, struct string_list_item *item, void *print_item_data);
5152
void *print_item_data;
@@ -54,7 +55,7 @@ struct list_options {
5455
static void list(struct add_i_state *s, struct string_list *list,
5556
struct list_options *opts)
5657
{
57-
int i;
58+
int i, last_lf = 0;
5859

5960
if (!list->nr)
6061
return;
@@ -65,8 +66,98 @@ static void list(struct add_i_state *s, struct string_list *list,
6566

6667
for (i = 0; i < list->nr; i++) {
6768
opts->print_item(i, list->items + i, opts->print_item_data);
69+
70+
if ((opts->columns) && ((i + 1) % (opts->columns))) {
71+
putchar('\t');
72+
last_lf = 0;
73+
}
74+
else {
75+
putchar('\n');
76+
last_lf = 1;
77+
}
78+
}
79+
80+
if (!last_lf)
6881
putchar('\n');
82+
}
83+
struct list_and_choose_options {
84+
struct list_options list_opts;
85+
86+
const char *prompt;
87+
};
88+
89+
#define LIST_AND_CHOOSE_ERROR (-1)
90+
#define LIST_AND_CHOOSE_QUIT (-2)
91+
92+
/*
93+
* Returns the selected index.
94+
*
95+
* If an error occurred, returns `LIST_AND_CHOOSE_ERROR`. Upon EOF,
96+
* `LIST_AND_CHOOSE_QUIT` is returned.
97+
*/
98+
static ssize_t list_and_choose(struct add_i_state *s, struct string_list *items,
99+
struct list_and_choose_options *opts)
100+
{
101+
struct strbuf input = STRBUF_INIT;
102+
ssize_t res = LIST_AND_CHOOSE_ERROR;
103+
104+
for (;;) {
105+
char *p;
106+
107+
strbuf_reset(&input);
108+
109+
list(s, items, &opts->list_opts);
110+
111+
printf("%s%s", opts->prompt, "> ");
112+
fflush(stdout);
113+
114+
if (strbuf_getline(&input, stdin) == EOF) {
115+
putchar('\n');
116+
res = LIST_AND_CHOOSE_QUIT;
117+
break;
118+
}
119+
strbuf_trim(&input);
120+
121+
if (!input.len)
122+
break;
123+
124+
p = input.buf;
125+
for (;;) {
126+
size_t sep = strcspn(p, " \t\r\n,");
127+
ssize_t index = -1;
128+
129+
if (!sep) {
130+
if (!*p)
131+
break;
132+
p++;
133+
continue;
134+
}
135+
136+
if (isdigit(*p)) {
137+
char *endp;
138+
index = strtoul(p, &endp, 10) - 1;
139+
if (endp != p + sep)
140+
index = -1;
141+
}
142+
143+
if (p[sep])
144+
p[sep++] = '\0';
145+
if (index < 0 || index >= items->nr)
146+
printf(_("Huh (%s)?\n"), p);
147+
else {
148+
res = index;
149+
break;
150+
}
151+
152+
p += sep;
153+
}
154+
155+
if (res != LIST_AND_CHOOSE_ERROR)
156+
break;
69157
}
158+
159+
strbuf_release(&input);
160+
return res;
70161
}
71162

72163
struct adddel {
@@ -252,20 +343,48 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
252343
return 0;
253344
}
254345

346+
typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
347+
struct string_list *files,
348+
struct list_options *opts);
349+
350+
static void print_command_item(int i, struct string_list_item *item,
351+
void *print_command_item_data)
352+
{
353+
printf(" %2d: %s", i + 1, item->string);
354+
}
355+
255356
int run_add_i(struct repository *r, const struct pathspec *ps)
256357
{
257358
struct add_i_state s = { NULL };
359+
struct list_and_choose_options main_loop_opts = {
360+
{ 4, N_("*** Commands ***"), print_command_item, NULL },
361+
N_("What now")
362+
};
363+
struct {
364+
const char *string;
365+
command_t command;
366+
} command_list[] = {
367+
{ "status", run_status },
368+
};
369+
struct string_list commands = STRING_LIST_INIT_NODUP;
370+
258371
struct print_file_item_data print_file_item_data = {
259372
"%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
260373
};
261374
struct list_options opts = {
262-
NULL, print_file_item, &print_file_item_data
375+
0, NULL, print_file_item, &print_file_item_data
263376
};
264377
struct strbuf header = STRBUF_INIT;
265378
struct string_list files = STRING_LIST_INIT_DUP;
379+
ssize_t i;
266380
int res = 0;
267381

382+
for (i = 0; i < ARRAY_SIZE(command_list); i++)
383+
string_list_append(&commands, command_list[i].string)
384+
->util = command_list[i].command;
385+
268386
init_add_i_state(&s, r);
387+
269388
strbuf_addstr(&header, " ");
270389
strbuf_addf(&header, print_file_item_data.modified_fmt,
271390
_("staged"), _("unstaged"), _("path"));
@@ -279,11 +398,25 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
279398

280399
res = run_status(&s, ps, &files, &opts);
281400

401+
for (;;) {
402+
i = list_and_choose(&s, &commands, &main_loop_opts);
403+
if (i == LIST_AND_CHOOSE_QUIT) {
404+
printf(_("Bye.\n"));
405+
res = 0;
406+
break;
407+
}
408+
if (i != LIST_AND_CHOOSE_ERROR) {
409+
command_t command = commands.items[i].util;
410+
res = command(&s, ps, &files, &opts);
411+
}
412+
}
413+
282414
string_list_clear(&files, 1);
283415
strbuf_release(&print_file_item_data.buf);
284416
strbuf_release(&print_file_item_data.index);
285417
strbuf_release(&print_file_item_data.worktree);
286418
strbuf_release(&header);
419+
string_list_clear(&commands, 0);
287420

288421
return res;
289422
}

0 commit comments

Comments
 (0)