Subject: [PATCH v2 01/41] parse-options: support --git-completion-helper

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Wed, 31 Jan 2018 18:05:07 +0700

This option is designed to be used by git-completion.bash. For many
simple cases, what we do in there is usually

    __gitcomp "lots of completion options"

which has to be manually updated when a new user-visible option is
added. With support from parse-options, we can write

    __gitcomp "$(git command --git-completion-helper)"

and get that list directly from the parser for free. Dangerous/Unpopular
options could be hidden with the new "NOCOMPLETE" flag.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 parse-options.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 parse-options.h |  5 ++++-
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/parse-options.c b/parse-options.c
index fca7159646..29f4defdd6 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -425,6 +425,46 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
 	parse_options_check(options);
 }
 
+/*
+ * TODO: we are not completing the --no-XXX form yet because there are
+ * many options that do not suppress it properly.
+ */
+static int show_gitcomp(struct parse_opt_ctx_t *ctx,
+			const struct option *opts)
+{
+	for (; opts->type != OPTION_END; opts++) {
+		const char *suffix = "";
+
+		if (!opts->long_name)
+			continue;
+		if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
+			continue;
+
+		switch (opts->type) {
+		case OPTION_GROUP:
+			continue;
+		case OPTION_STRING:
+		case OPTION_FILENAME:
+		case OPTION_INTEGER:
+		case OPTION_MAGNITUDE:
+		case OPTION_CALLBACK:
+			if (opts->flags & PARSE_OPT_NOARG)
+				break;
+			if (opts->flags & PARSE_OPT_OPTARG)
+				break;
+			if (opts->flags & PARSE_OPT_LASTARG_DEFAULT)
+				break;
+			suffix = "=";
+			break;
+		default:
+			break;
+		}
+		printf(" --%s%s", opts->long_name, suffix);
+	}
+	fputc('\n', stdout);
+	exit(0);
+}
+
 static int usage_with_options_internal(struct parse_opt_ctx_t *,
 				       const char * const *,
 				       const struct option *, int, int);
@@ -455,6 +495,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
 		if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h"))
 			goto show_usage;
 
+		/* lone --git-completion-helper is asked by git-completion.bash */
+		if (ctx->total == 1 && !strcmp(arg + 1, "-git-completion-helper"))
+			return show_gitcomp(ctx, options);
+
 		if (arg[1] != '-') {
 			ctx->opt = arg + 1;
 			switch (parse_short_opt(ctx, options)) {
diff --git a/parse-options.h b/parse-options.h
index af711227ae..983de27cf0 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -38,7 +38,8 @@ enum parse_opt_option_flags {
 	PARSE_OPT_LASTARG_DEFAULT = 16,
 	PARSE_OPT_NODASH = 32,
 	PARSE_OPT_LITERAL_ARGHELP = 64,
-	PARSE_OPT_SHELL_EVAL = 256
+	PARSE_OPT_SHELL_EVAL = 256,
+	PARSE_OPT_NOCOMPLETE = 512
 };
 
 struct option;
@@ -89,6 +90,8 @@ typedef int parse_opt_ll_cb(struct parse_opt_ctx_t *ctx,
  *   PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets
  *				(i.e. '<argh>') in the help message.
  *				Useful for options with multiple parameters.
+ *   PARSE_OPT_NOCOMPLETE: by default all visible options are completable
+ *			   git-completion.bash. This option suppresses that.
  *
  * `callback`::
  *   pointer to the callback to use for OPTION_CALLBACK or
-- 
2.16.1.205.g271f633410