Re: [PATCH v6 4/7] git-clean: use a git-add-interactive compatible UI
From: Jiang Xin <hidden>
Date: 2016-06-15 22:57:09
2013/5/7 Jiang Xin [off-list ref]:
quoted hunk ↗ jump to hunk
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 <redacted> --- 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