git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH v6 4/7] git-clean: use a git-add-interactive compatible UI

From
Jiang Xin <worldhello.net@gmail.com>
Date
May 7, 2013, 04:16 UTC
Message-ID
<CANYiYbEE90zvhoRp3utOzRWXnpVGTcaruWDuCCHE77FmGwB2_w@mail.gmail.com>
In-Reply-To
<217e7618e9b9d50a50461959b38984ac8be88651.1367867498.git.worldhello.net@gmail.com>
2013/5/7 Jiang Xin <worldhello.net@gmail.com>:
Show 69 quoted lines
> 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.net@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.
Show 7 quoted lines
> +       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.
Show 6 quoted lines
> +       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.
Show 81 quoted lines
> +}
> +
> +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
Show 10 quoted lines
> +                       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);
Show 39 quoted lines
> +               }
> +
> +               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
Show 91 quoted lines
> +               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);
Show 14 quoted lines
> +
> +               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;
Show 13 quoted lines
> +               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
Previous: Jiang XinNext: Junio C Hamano
Message 19 of 22 in “interactive git clean”
  1. 0/3 interactive git cleanJiang Xin, May 3, 2013
  2. 1/3 Add support for -i/--interactive to git-cleanJiang Xin, May 3, 2013
  3. 2/3 Show items of interactive git-clean in columnsJiang Xin, May 3, 2013
  4. 3/3 Add colors to interactive git-cleanJiang Xin, May 3, 2013
  5. Eric SunshineMay 3, 2013
  6. Jiang XinMay 4, 2013
  7. Eric SunshineMay 5, 2013
  8. Matthieu MoyMay 6, 2013
  9. Eric SunshineMay 6, 2013
  10. 0/7 interactive git cleanJiang Xin, May 6, 2013
  11. 1/7 Add support for -i/--interactive to git-cleanJiang Xin, May 6, 2013
  12. 2/7 Show items of interactive git-clean in columnsJiang Xin, May 6, 2013
  13. 3/7 Add colors to interactive git-cleanJiang Xin, May 6, 2013
  14. 4/7 git-clean: use a git-add-interactive compatible UIJiang Xin, May 6, 2013
  15. 5/7 git-clean: interactive cleaning by select numbersJiang Xin, May 6, 2013
  16. 6/7 git-clean: rm -i style interactive cleaningJiang Xin, May 6, 2013
  17. 7/7 git-clean: update document for interactive git-cleanJiang Xin, May 6, 2013
  18. Jiang XinMay 7, 2013
  19. Jiang XinMay 7, 2013
  20. Junio C HamanoMay 7, 2013
  21. Jiang XinMay 8, 2013
  22. Junio C HamanoMay 3, 2013

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.