Skip to content

Commit 3eec219

Browse files
committed
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. Apart from the "and choose" part, there are more differences between the way the `status` command calls the `list_and_choose()` function in the Perl version of `git add -i` compared to the other callers of said function. The most important ones: - The list is not only shown, but the user is also asked to make a choice, possibly selecting multiple entries. - The list of items is prefixed with a marker indicating what items have been selected, if multi-selection is allowed. - Initially, for each item a unique prefix (if there exists any within the given parameters) is determined, and shown in the list, and accepted as a shortcut for the selection. These features will be implemented later, except the part where the user can choose a command. At this stage, though, the built-in `git add -i` still only supports the `status` command, with the remaining commands to follow over the course of the next commits. In addition, we also modify `list()` to support displaying the commands in columns, even if there is currently only one. The Perl script `git-add--interactive.perl` mixed the purposes of the "list" and the "and choose" part into the same function. In the C version, we will keep them separate instead, calling the `list()` function from the `list_and_choose()` function. 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]>
1 parent 83d92a9 commit 3eec219

File tree

1 file changed

+127
-2
lines changed

1 file changed

+127
-2
lines changed

add-interactive.c

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ struct item {
5151
};
5252

5353
struct list_options {
54+
int columns;
5455
const char *header;
5556
void (*print_item)(int i, struct item *item, void *print_item_data);
5657
void *print_item_data;
@@ -59,7 +60,7 @@ struct list_options {
5960
static void list(struct item **list, size_t nr,
6061
struct add_i_state *s, struct list_options *opts)
6162
{
62-
int i;
63+
int i, last_lf = 0;
6364

6465
if (!nr)
6566
return;
@@ -70,8 +71,97 @@ static void list(struct item **list, size_t nr,
7071

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

77167
struct adddel {
@@ -285,17 +375,40 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
285375
return 0;
286376
}
287377

378+
static void print_command_item(int i, struct item *item,
379+
void *print_command_item_data)
380+
{
381+
printf(" %2d: %s", i + 1, item->name);
382+
}
383+
384+
struct command_item {
385+
struct item item;
386+
int (*command)(struct add_i_state *s, const struct pathspec *ps,
387+
struct file_list *files, struct list_options *opts);
388+
};
389+
288390
int run_add_i(struct repository *r, const struct pathspec *ps)
289391
{
290392
struct add_i_state s = { NULL };
393+
struct list_and_choose_options main_loop_opts = {
394+
{ 4, N_("*** Commands ***"), print_command_item, NULL },
395+
N_("What now")
396+
};
397+
struct command_item
398+
status = { { "status" }, run_status };
399+
struct command_item *commands[] = {
400+
&status
401+
};
402+
291403
struct print_file_item_data print_file_item_data = {
292404
"%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
293405
};
294406
struct list_options opts = {
295-
NULL, print_file_item, &print_file_item_data
407+
0, NULL, print_file_item, &print_file_item_data
296408
};
297409
struct strbuf header = STRBUF_INIT;
298410
struct file_list files = { NULL };
411+
ssize_t i;
299412
int res = 0;
300413

301414
if (init_add_i_state(r, &s))
@@ -310,6 +423,18 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
310423
if (run_status(&s, ps, &files, &opts) < 0)
311424
res = -1;
312425

426+
for (;;) {
427+
i = list_and_choose((struct item **)commands,
428+
ARRAY_SIZE(commands), &s, &main_loop_opts);
429+
if (i == LIST_AND_CHOOSE_QUIT) {
430+
printf(_("Bye.\n"));
431+
res = 0;
432+
break;
433+
}
434+
if (i != LIST_AND_CHOOSE_ERROR)
435+
res = commands[i]->command(&s, ps, &files, &opts);
436+
}
437+
313438
release_file_list(&files);
314439
strbuf_release(&print_file_item_data.buf);
315440
strbuf_release(&print_file_item_data.index);

0 commit comments

Comments
 (0)