2013/5/7 Jiang Xin <[email protected]>:
> Rewrite menu using a new method `list_and_choose`, which is borrowed
> from `git-add--interactive.perl`. We can reused this method later for
> more actions.
>
> Please NOTE:
>
> * Method `list_and_choose` return an array of integers, and
> * it is up to you to free the allocated memory of the array.
> * The array ends with EOF.
> * If user pressed CTRL-D (i.e. EOF), no selection returned.
>
> Signed-off-by: Jiang Xin <[email protected]>
> ---
> builtin/clean.c | 410
> ++++++++++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 367 insertions(+), 43 deletions(-)
>
> diff --git a/builtin/clean.c b/builtin/clean.c
> index 6bda3..3b9f3 100644
> --- a/builtin/clean.c
> +++ b/builtin/clean.c
> @@ -16,6 +16,35 @@
> #include "column.h"
> #include "color.h"
>
> +#define MENU_OPTS_SINGLETON 01
> +#define MENU_OPTS_IMMEDIATE 02
> +#define MENU_OPTS_LIST_ONLY 04
> +
> +#define MENU_RETURN_NO_LOOP 10
> +
> +struct menu_opts {
> + const char *header;
> + const char *prompt;
> + int flag;
> +};
> +
> +enum menu_stuff_type {
> + MENU_STUFF_TYPE_STRING_LIST = 1,
> + MENU_STUFF_TYPE_MENU_ITEM
> +};
> +
> +struct menu_stuff {
> + enum menu_stuff_type type;
> + int nr;
> + void *stuff;
> +};
> +
> +struct menu_item {
> + char hotkey;
> + char *title;
> + int (*fn)();
> +};
> +
> static int force = -1; /* unset */
> static int interactive;
> static struct string_list del_list = STRING_LIST_INIT_DUP;
> @@ -240,12 +269,284 @@ void pretty_print_dels()
> copts.indent = " ";
> copts.padding = 2;
> print_columns(&list, colopts, &copts);
> - putchar('\n');
> strbuf_release(&buf);
> string_list_clear(&list, 0);
> }
>
> -void edit_by_patterns_cmd()
> +void pretty_print_menus(struct string_list *menu_list)
> +{
> + struct strbuf buf = STRBUF_INIT;
unused buf should be deleted.
> + unsigned int local_colopts = 0;
> + struct column_options copts;
> +
> + /*
> + * always enable column display, we only consult column.*
> + * about layout strategy and stuff
> + */
remove the above comments.
> + local_colopts = COL_ENABLED | COL_ROW;
> + memset(&copts, 0, sizeof(copts));
> + copts.indent = " ";
> + copts.padding = 2;
> + print_columns(menu_list, local_colopts, &copts);
> + strbuf_release(&buf);
remove strbuf_release of unused variable : buf.
> +}
> +
> +void prompt_help_cmd(int singleton)
> +{
> + clean_print_color(CLEAN_COLOR_HELP);
> + printf_ln(singleton ?
> + _("Prompt help:\n"
> + "1 - select a numbered item\n"
> + "foo - select item based on unique prefix\n"
> + " - (empty) select nothing") :
> + _("Prompt help:\n"
> + "1 - select a single item\n"
> + "3-5 - select a range of items\n"
> + "2-3,6-9 - select multiple ranges\n"
> + "foo - select item based on unique prefix\n"
> + "-... - unselect specified items\n"
> + "* - choose all items\n"
> + " - (empty) finish selecting"));
> + clean_print_color(CLEAN_COLOR_RESET);
> +}
> +
> +/*
> + * Implement a git-add-interactive compatible UI, which is borrowed
> + * from git-add--interactive.perl.
> + *
> + * Return value:
> + *
> + * - Return an array of integers
> + * - , and it is up to you to free the allocated memory.
> + * - The array ends with EOF.
> + * - If user pressed CTRL-D (i.e. EOF), no selection returned.
> + */
> +int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
> +{
> + static struct string_list menu_list = STRING_LIST_INIT_DUP;
> + struct strbuf menu = STRBUF_INIT;
> + struct strbuf choice = STRBUF_INIT;
> + struct strbuf **choice_list;
> + int *chosen, *result;
> + char *p;
> + int nr = 0;
> + int i, j;
> + int eof = 0;
> +
> + chosen = xmalloc(sizeof(int) * stuff->nr);
> + memset(chosen, 0, sizeof(int) * stuff->nr);
> +
> + while (1) {
> + int i = 0, j = 0;
> + string_list_clear(&menu_list, 0);
> +
> + if (opts->header) {
> + printf_ln("%s%s%s",
> + clean_get_color(CLEAN_COLOR_HEADER),
> + opts->header,
> + clean_get_color(CLEAN_COLOR_RESET));
> + }
> +
> + /* highlight hotkey in menu */
> + if (MENU_STUFF_TYPE_MENU_ITEM == stuff->type) {
> + struct menu_item *item;
> +
> + item = (struct menu_item *)stuff->stuff;
> + for (i = 0; i < stuff->nr; i++, item++) {
> + p = item->title;
> + strbuf_addf(&menu, "%s%2d: ", chosen[i] ? "*"
> : " ", i+1);
> + for (; *p; p++) {
> + if (*p == item->hotkey) {
> + strbuf_addstr(&menu,
> clean_get_color(CLEAN_COLOR_PROMPT));
> + strbuf_addch(&menu, *p);
> + strbuf_addstr(&menu,
> clean_get_color(CLEAN_COLOR_RESET));
> + } else {
> + strbuf_addch(&menu, *p);
> + }
> + }
> + string_list_append(&menu_list, menu.buf);
> + strbuf_reset(&menu);
> + }
> + } else if (MENU_STUFF_TYPE_STRING_LIST == stuff->type) {
> + struct string_list_item *item;
> + struct strbuf buf = STRBUF_INIT;
should call strbuf_release later
> + i = 0;
> +
> + for_each_string_list_item(item, (struct string_list
> *)stuff->stuff) {
> + const char *qname;
> +
> + qname = quote_path_relative(item->string, -1,
> &buf, *the_prefix);
> + strbuf_addf(&menu, "%s%2d: %s", chosen[i] ?
> "*" : " ", ++i, qname);
> + string_list_append(&menu_list, menu.buf);
> + strbuf_reset(&menu);
> + }
+ strbuf_release(&buf);
> + }
> +
> + pretty_print_menus(&menu_list);
> +
> + if (opts->flag & MENU_OPTS_LIST_ONLY)
> + break;
> +
> + if (opts->prompt) {
> + printf("%s%s%s%s",
> + clean_get_color(CLEAN_COLOR_PROMPT),
> + opts->prompt,
> + opts->flag & MENU_OPTS_SINGLETON ? "> " : ">>
> ",
> + clean_get_color(CLEAN_COLOR_RESET));
> + }
> +
> + if (strbuf_getline(&choice, stdin, '\n') != EOF) {
> + if (!(opts->flag & MENU_OPTS_SINGLETON)) {
> + char *p = choice.buf;
> + do {
> + if (*p == ',')
> + *p = ' ';
> + } while (*p++);
> + }
> + strbuf_trim(&choice);
> + } else {
> + eof = 1;
> + break;
> + }
> +
> + /* help for prompt */
> + if (!strcmp(choice.buf, "?")) {
> + prompt_help_cmd(opts->flag & MENU_OPTS_SINGLETON);
> + continue;
> + }
> +
> + if (!(opts->flag & MENU_OPTS_SINGLETON) && !choice.len)
> + break;
> +
> + choice_list = strbuf_split_max(&choice, ' ', 0);
Should be freed later
> + for (i = 0; choice_list[i]; i++) {
> + int choose = 1;
> + int bottom = 0, top = 0;
> + char *p;
> + int is_range = 0;
> + int is_number = 1;
> +
> + strbuf_trim(choice_list[i]);
> + if (!choice_list[i]->len)
> + continue;
> +
> + /* Input that begins with '-'; unchoose */
> + if (*choice_list[i]->buf == '-') {
> + choose = 0;
> + strbuf_remove(choice_list[i], 0, 1);
> + }
> +
> + p = choice_list[i]->buf;
> + for(; *p; p++) {
> + if ('-' == *p) {
> + if (!is_range) {
> + is_range = 1;
> + is_number = 0;
> + } else {
> + is_number = 0;
> + is_range = 0;
> + break;
> + }
> + } else if (!isdigit(*p)) {
> + is_number = 0;
> + is_range = 0;
> + break;
> + }
> + }
> +
> + if (is_number) {
> + bottom = atoi(choice_list[i]->buf);
> + top = bottom;
> + } else if (is_range) {
> + bottom = atoi(choice_list[i]->buf);
> + if (!*(strchr(choice_list[i]->buf, '-') + 1))
> {
> + top = stuff->nr - 1;
> + } else {
> + top =
> atoi(strchr(choice_list[i]->buf, '-') + 1);
> + }
> + } else if (!strcmp(choice_list[i]->buf, "*")) {
> + bottom = 1;
> + top = stuff->nr;
> + } else {
> + if (MENU_STUFF_TYPE_MENU_ITEM == stuff->type)
> {
> + struct menu_item *item;
> +
> + item = (struct menu_item
> *)stuff->stuff;
> + for (j = 0; j < stuff->nr; j++,
> item++) {
> + if ((choice_list[i]->len == 1
> &&
> + *choice_list[i]->buf ==
> item->hotkey) ||
> +
> !strcasecmp(choice_list[i]->buf, item->title)) {
> + bottom = j + 1;
> + top = bottom;
> + break;
> + }
> + }
> + } else if (MENU_STUFF_TYPE_STRING_LIST ==
> stuff->type) {
> + struct string_list_item *item;
> +
> + item = ((struct string_list
> *)stuff->stuff)->items;
> + for (j = 0; j < stuff->nr; j++,
> item++) {
> + if
> (!strcasecmp(choice_list[i]->buf, item->string)) {
> + bottom = j + 1;
> + top = bottom;
> + break;
> + }
> + }
> + }
> + }
> +
> + if (top <= 0 || bottom <= 0 || top > stuff-> nr ||
> bottom > top ||
> + (opts->flag & MENU_OPTS_SINGLETON && bottom !=
> top)) {
> + printf_ln("%sHuh (%s)?%s",
> + clean_get_color(CLEAN_COLOR_ERROR),
> + choice_list[i]->buf,
> + clean_get_color(CLEAN_COLOR_RESET));
> + continue;
> + }
> +
> + /* A range can be specified like 5-7 or 5-. */
> + for (j = bottom; j <= top; j++) {
> + chosen[j-1] = choose;
> + nr++;
> + }
> + }
+ strbuf_list_free(choice_list);
> +
> + if (opts->flag & MENU_OPTS_SINGLETON) {
> + if (nr)
> + break;
> + } else if (opts->flag & MENU_OPTS_IMMEDIATE) {
> + break;
> + }
> + }
> +
> +
> + if (eof) {
> + result = xmalloc(sizeof(int) * 2);
> + result[0] = EOF;
> + result[1] = 0;
Allocate one element is OK, like:
+ result = xmalloc(sizeof(int));
+ *result = EOF;
> + } else {
> + result = xmalloc(sizeof(int) * (nr + 1));
> + memset(result, 0, sizeof(int) * (nr + 1));
Add initial for j here:
+ j = 0;
> + for (i = 0, j = 0; i < stuff->nr && j < nr; i++) {
> + if (chosen[i])
> + result[j++] = i;
> + }
> + result[j] = EOF;
> + }
> +
> + free(chosen);
> + string_list_clear(&menu_list, 0);
> + strbuf_release(&menu);
> + strbuf_release(&choice);
> + return result;
> +}
--
Jiang Xin
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html