Skip to content

Commit 5031686

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 3cac1fc commit 5031686

File tree

1 file changed

+133
-2
lines changed

1 file changed

+133
-2
lines changed

add-interactive.c

Lines changed: 133 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,96 @@ 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, *endp;
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+
index = strtoul(p, &endp, 10) - 1;
138+
if (endp != p + sep)
139+
index = -1;
140+
}
141+
142+
p[sep] = '\0';
143+
if (index < 0 || index >= items->nr)
144+
printf(_("Huh (%s)?\n"), p);
145+
else {
146+
res = index;
147+
break;
148+
}
149+
150+
p += sep + 1;
151+
}
152+
153+
if (res != LIST_AND_CHOOSE_ERROR)
154+
break;
69155
}
156+
157+
strbuf_release(&input);
158+
return res;
70159
}
71160

72161
struct adddel {
@@ -252,20 +341,48 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
252341
return 0;
253342
}
254343

344+
typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps,
345+
struct string_list *files,
346+
struct list_options *opts);
347+
348+
static void print_command_item(int i, struct string_list_item *item,
349+
void *print_command_item_data)
350+
{
351+
printf(" %2d: %s", i + 1, item->string);
352+
}
353+
255354
int run_add_i(struct repository *r, const struct pathspec *ps)
256355
{
257356
struct add_i_state s = { NULL };
357+
struct list_and_choose_options main_loop_opts = {
358+
{ 4, N_("*** Commands ***"), print_command_item, NULL },
359+
N_("What now")
360+
};
361+
struct {
362+
const char *string;
363+
command_t command;
364+
} command_list[] = {
365+
{ "status", run_status },
366+
};
367+
struct string_list commands = STRING_LIST_INIT_NODUP;
368+
258369
struct print_file_item_data print_file_item_data = {
259370
"%12s %12s %s", STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
260371
};
261372
struct list_options opts = {
262-
NULL, print_file_item, &print_file_item_data
373+
0, NULL, print_file_item, &print_file_item_data
263374
};
264375
struct strbuf header = STRBUF_INIT;
265376
struct string_list files = STRING_LIST_INIT_DUP;
377+
ssize_t i;
266378
int res = 0;
267379

380+
for (i = 0; i < ARRAY_SIZE(command_list); i++)
381+
string_list_append(&commands, command_list[i].string)
382+
->util = command_list[i].command;
383+
268384
init_add_i_state(&s, r);
385+
269386
strbuf_addstr(&header, " ");
270387
strbuf_addf(&header, print_file_item_data.modified_fmt,
271388
_("staged"), _("unstaged"), _("path"));
@@ -279,11 +396,25 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
279396

280397
res = run_status(&s, ps, &files, &opts);
281398

399+
for (;;) {
400+
i = list_and_choose(&s, &commands, &main_loop_opts);
401+
if (i == LIST_AND_CHOOSE_QUIT) {
402+
printf(_("Bye.\n"));
403+
res = 0;
404+
break;
405+
}
406+
if (i != LIST_AND_CHOOSE_ERROR) {
407+
command_t command = commands.items[i].util;
408+
res = command(&s, ps, &files, &opts);
409+
}
410+
}
411+
282412
string_list_clear(&files, 1);
283413
strbuf_release(&print_file_item_data.buf);
284414
strbuf_release(&print_file_item_data.index);
285415
strbuf_release(&print_file_item_data.worktree);
286416
strbuf_release(&header);
417+
string_list_clear(&commands, 0);
287418

288419
return res;
289420
}

0 commit comments

Comments
 (0)