2013/5/7 Jiang Xin <worldhello....@gmail.com>:
> 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 <worldhello....@gmail.com>
> ---
>  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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to