Skip to content

Commit

Permalink
built-in add -i: implement the main loop
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
dscho authored and gitster committed Nov 18, 2019
1 parent 1daaebc commit 6348bfb
Showing 1 changed file with 135 additions and 2 deletions.
137 changes: 135 additions & 2 deletions add-interactive.c
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ static void init_add_i_state(struct add_i_state *s, struct repository *r)
}

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;
Expand All @@ -54,7 +55,7 @@ struct list_options {
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;
Expand All @@ -65,8 +66,98 @@ static void list(struct add_i_state *s, struct string_list *list,

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 {
Expand Down Expand Up @@ -252,20 +343,48 @@ static int run_status(struct add_i_state *s, const struct pathspec *ps,
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"));
Expand All @@ -279,11 +398,25 @@ int run_add_i(struct repository *r, const struct pathspec *ps)

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;
}

0 comments on commit 6348bfb

Please sign in to comment.