From e8e5d294dc6e3b6b32132cc8018d01ce35ad0af0 Mon Sep 17 00:00:00 2001 From: René Scharfe Date: Sat, 5 Aug 2023 16:40:59 +0200 Subject: parse-options: show negatability of options in short help MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a "[no-]" prefix to options without the flag PARSE_OPT_NONEG to document the fact that you can negate them. This looks a bit strange for options that already start with "no-", e.g. for the option --no-name of git show-branch: --[no-]no-name suppress naming strings You can actually use --no-no-name as an alias of --name, so the short help is not wrong. If we strip off any of the "no-"s, we lose either the ability to see if the remaining one belongs to the documented variant or to see if it can be negated. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- parse-options.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'parse-options.c') diff --git a/parse-options.c b/parse-options.c index 87c9fae634..b750bf91cd 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1137,8 +1137,14 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t } if (opts->long_name && opts->short_name) pos += fprintf(outfile, ", "); - if (opts->long_name) - pos += fprintf(outfile, "--%s", opts->long_name); + if (opts->long_name) { + const char *long_name = opts->long_name; + if (opts->flags & PARSE_OPT_NONEG) + pos += fprintf(outfile, "--%s", long_name); + else + pos += fprintf(outfile, "--[no-]%s", long_name); + } + if (opts->type == OPTION_NUMBER) pos += utf8_fprintf(outfile, _("-NUM")); -- cgit 1.2.3-korg From 652a6b15bc1cd0f837bc969e87fd31f3e88413f6 Mon Sep 17 00:00:00 2001 From: René Scharfe Date: Sat, 5 Aug 2023 16:43:04 +0200 Subject: parse-options: factor out usage_indent() and usage_padding() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract functions for printing spaces before and after options. We'll need them in the next commit. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- parse-options.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'parse-options.c') diff --git a/parse-options.c b/parse-options.c index b750bf91cd..4b76fc81e9 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1020,9 +1020,28 @@ static int usage_argh(const struct option *opts, FILE *outfile) return utf8_fprintf(outfile, s, opts->argh ? _(opts->argh) : _("...")); } +static int usage_indent(FILE *outfile) +{ + return fprintf(outfile, " "); +} + #define USAGE_OPTS_WIDTH 24 #define USAGE_GAP 2 +static void usage_padding(FILE *outfile, size_t pos) +{ + int pad; + if (pos == USAGE_OPTS_WIDTH + 1) + pad = -1; + else if (pos <= USAGE_OPTS_WIDTH) + pad = USAGE_OPTS_WIDTH - pos; + else { + fputc('\n', outfile); + pad = USAGE_OPTS_WIDTH; + } + fprintf(outfile, "%*s", pad + USAGE_GAP, ""); +} + static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx, const char * const *usagestr, const struct option *opts, @@ -1108,7 +1127,6 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t for (; opts->type != OPTION_END; opts++) { size_t pos; - int pad; const char *cp, *np; if (opts->type == OPTION_SUBCOMMAND) @@ -1128,7 +1146,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t need_newline = 0; } - pos = fprintf(outfile, " "); + pos = usage_indent(outfile); if (opts->short_name) { if (opts->flags & PARSE_OPT_NODASH) pos += fprintf(outfile, "%c", opts->short_name); @@ -1152,16 +1170,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t !(opts->flags & PARSE_OPT_NOARG)) pos += usage_argh(opts, outfile); - if (pos == USAGE_OPTS_WIDTH + 1) - pad = -1; - else if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', outfile); - pad = USAGE_OPTS_WIDTH; - } if (opts->type == OPTION_ALIAS) { - fprintf(outfile, "%*s", pad + USAGE_GAP, ""); + usage_padding(outfile, pos); fprintf_ln(outfile, _("alias of --%s"), (const char *)opts->value); continue; @@ -1169,12 +1179,11 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t for (cp = _(opts->help); *cp; cp = np) { np = strchrnul(cp, '\n'); - fprintf(outfile, - "%*s%.*s\n", pad + USAGE_GAP, "", - (int)(np - cp), cp); + usage_padding(outfile, pos); + fprintf(outfile, "%.*s\n", (int)(np - cp), cp); if (*np) np++; - pad = USAGE_OPTS_WIDTH; + pos = 0; } } fputc('\n', outfile); -- cgit 1.2.3-korg From 2a409a1d1250c8a9fcac7beaa58ae80881dda2dc Mon Sep 17 00:00:00 2001 From: René Scharfe Date: Sat, 5 Aug 2023 16:44:45 +0200 Subject: parse-options: no --[no-]no-... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid showing an optional "no-" for options that already start with a "no-" in the short help, as that double negation is confusing. Document the opposite variant on its own line with a generated help text instead, unless it's defined and documented explicitly already. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- parse-options.c | 25 ++++++++++++++++++++++++- t/t0040-parse-options.sh | 3 ++- t/t1502/optionspec-neg.help | 3 ++- 3 files changed, 28 insertions(+), 3 deletions(-) (limited to 'parse-options.c') diff --git a/parse-options.c b/parse-options.c index 4b76fc81e9..4a8d380ceb 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1042,11 +1042,22 @@ static void usage_padding(FILE *outfile, size_t pos) fprintf(outfile, "%*s", pad + USAGE_GAP, ""); } +static const struct option *find_option_by_long_name(const struct option *opts, + const char *long_name) +{ + for (; opts->type != OPTION_END; opts++) { + if (opts->long_name && !strcmp(opts->long_name, long_name)) + return opts; + } + return NULL; +} + static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *ctx, const char * const *usagestr, const struct option *opts, int full, int err) { + const struct option *all_opts = opts; FILE *outfile = err ? stderr : stdout; int need_newline; @@ -1128,6 +1139,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t for (; opts->type != OPTION_END; opts++) { size_t pos; const char *cp, *np; + const char *positive_name = NULL; if (opts->type == OPTION_SUBCOMMAND) continue; @@ -1157,7 +1169,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t pos += fprintf(outfile, ", "); if (opts->long_name) { const char *long_name = opts->long_name; - if (opts->flags & PARSE_OPT_NONEG) + if ((opts->flags & PARSE_OPT_NONEG) || + skip_prefix(long_name, "no-", &positive_name)) pos += fprintf(outfile, "--%s", long_name); else pos += fprintf(outfile, "--[no-]%s", long_name); @@ -1185,6 +1198,16 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t np++; pos = 0; } + + if (positive_name) { + if (find_option_by_long_name(all_opts, positive_name)) + continue; + pos = usage_indent(outfile); + pos += fprintf(outfile, "--%s", positive_name); + usage_padding(outfile, pos); + fprintf_ln(outfile, _("opposite of --no-%s"), + positive_name); + } } fputc('\n', outfile); diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh index 1dfc431d52..a0ad6192d6 100755 --- a/t/t0040-parse-options.sh +++ b/t/t0040-parse-options.sh @@ -14,7 +14,8 @@ usage: test-tool parse-options A helper function for the parse-options API. --[no-]yes get a boolean - -D, --[no-]no-doubt begins with 'no-' + -D, --no-doubt begins with 'no-' + --doubt opposite of --no-doubt -B, --no-fear be brave -b, --[no-]boolean increment by one -4, --[no-]or4 bitwise-or boolean with ...0100 diff --git a/t/t1502/optionspec-neg.help b/t/t1502/optionspec-neg.help index 60ff3cdd00..7a29f8cb03 100644 --- a/t/t1502/optionspec-neg.help +++ b/t/t1502/optionspec-neg.help @@ -4,7 +4,8 @@ usage: some-command [options] ... some-command does foo and bar! --[no-]foo can be negated - --[no-]no-bar can be positivated + --no-bar can be positivated + --bar opposite of --no-bar --positive-only cannot be negated --no-negative cannot be positivated -- cgit 1.2.3-korg From 311c8ff11cebef1219e110743d9a57cb9831ab06 Mon Sep 17 00:00:00 2001 From: René Scharfe Date: Sat, 5 Aug 2023 16:52:42 +0200 Subject: parse-options: simplify usage_padding() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit c512643e67 (short help: allow a gap smaller than USAGE_GAP, 2023-07-18) effectively did away with the two-space gap between options and their description; one space is enough now. Incorporate USAGE_GAP into USAGE_OPTS_WIDTH, merge the two cases with enough space on the line and incorporate the newline into the format for the remaining case. The output remains the same. Signed-off-by: René Scharfe Signed-off-by: Junio C Hamano --- parse-options.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'parse-options.c') diff --git a/parse-options.c b/parse-options.c index 4a8d380ceb..b00d868816 100644 --- a/parse-options.c +++ b/parse-options.c @@ -1025,21 +1025,14 @@ static int usage_indent(FILE *outfile) return fprintf(outfile, " "); } -#define USAGE_OPTS_WIDTH 24 -#define USAGE_GAP 2 +#define USAGE_OPTS_WIDTH 26 static void usage_padding(FILE *outfile, size_t pos) { - int pad; - if (pos == USAGE_OPTS_WIDTH + 1) - pad = -1; - else if (pos <= USAGE_OPTS_WIDTH) - pad = USAGE_OPTS_WIDTH - pos; - else { - fputc('\n', outfile); - pad = USAGE_OPTS_WIDTH; - } - fprintf(outfile, "%*s", pad + USAGE_GAP, ""); + if (pos < USAGE_OPTS_WIDTH) + fprintf(outfile, "%*s", USAGE_OPTS_WIDTH - (int)pos, ""); + else + fprintf(outfile, "\n%*s", USAGE_OPTS_WIDTH, ""); } static const struct option *find_option_by_long_name(const struct option *opts, -- cgit 1.2.3-korg