From cc1e15bedaa1074eab95ad6c3189848e51cf0afd Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Sun, 21 Aug 2022 05:16:59 +0100 Subject: [PATCH] Break long help messages --- argparse.c | 63 ++++++++++++++++++++++++++++++++++++++++++-------- tests/basic.c | 4 ++++ tests/basic.sh | 3 +++ 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/argparse.c b/argparse.c index 9eec9ab94..66d748809 100644 --- a/argparse.c +++ b/argparse.c @@ -12,6 +12,8 @@ #include #include "argparse.h" +#define LINE_WRAP 80 + #define OPT_UNSET 1 #define OPT_LONG (1 << 1) @@ -286,6 +288,55 @@ argparse_parse(struct argparse *self, int argc, const char **argv) return self->cpidx + self->argc; } +void argparse_print_help(const char *help, int indent, int offset, int line_wrap) +{ + if (offset > indent) { + fputs("\n", stdout); + offset = 0; + } + + if (line_wrap - indent <= 1) { + line_wrap = indent + 2; + } + + size_t pos = 0; + size_t len = strlen(help); + while (pos < len) { + fprintf(stdout, "%*s", indent - offset, ""); + offset = 0; + size_t towrite = line_wrap - indent; + while (help[pos] == ' ') { + pos++; + } + if (pos + towrite > len) { + towrite = len - pos; + fwrite(help + pos, 1, towrite, stdout); + } else { + int space_break = towrite; + while (space_break > 0 && help[pos + space_break - 1] != ' ') { + space_break--; + } + + int print_hyphen = 0; + if (space_break == 0) { + print_hyphen = 1; + towrite--; + } else { + towrite = space_break; + } + + fwrite(help + pos, 1, towrite, stdout); + + if (print_hyphen) { + fputs("-", stdout); + } + } + + fputs("\n", stdout); + pos += towrite; + } +} + void argparse_usage(struct argparse *self) { @@ -329,7 +380,8 @@ argparse_usage(struct argparse *self) len += strlen("="); } len = (len + 3) - ((len + 3) & 3); - if (usage_opts_width < len) { + // If len >= LINE_WRAP / 2, we prints the option on a separate line. + if (len < LINE_WRAP / 2 && usage_opts_width < len) { usage_opts_width = len; } } @@ -338,7 +390,6 @@ argparse_usage(struct argparse *self) options = self->options; for (; options->type != ARGPARSE_OPT_END; options++) { size_t pos = 0; - size_t pad = 0; if (options->type == ARGPARSE_OPT_GROUP) { fputc('\n', stdout); fprintf(stdout, "%s", options->help); @@ -362,13 +413,7 @@ argparse_usage(struct argparse *self) } else if (options->type == ARGPARSE_OPT_STRING) { pos += fprintf(stdout, "="); } - if (pos <= usage_opts_width) { - pad = usage_opts_width - pos; - } else { - fputc('\n', stdout); - pad = usage_opts_width; - } - fprintf(stdout, "%*s%s\n", (int)pad + 2, "", options->help); + argparse_print_help(options->help, usage_opts_width + 2, pos, LINE_WRAP); } // print epilog diff --git a/tests/basic.c b/tests/basic.c index fcd4e34ff..be83b993f 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -22,6 +22,7 @@ main(int argc, const char **argv) float flt_num = 0.f; const char *path = NULL; int perms = 0; + int long_opt = 0; struct argparse_option options[] = { OPT_HELP(), OPT_GROUP("Basic options"), @@ -30,6 +31,9 @@ main(int argc, const char **argv) OPT_STRING('p', "path", &path, "path to read", NULL, 0, 0), OPT_INTEGER('i', "int", &int_num, "selected integer", NULL, 0, 0), OPT_FLOAT('s', "float", &flt_num, "selected float", NULL, 0, 0), + OPT_BOOLEAN('l', "long", &long_opt, "a option that has a really really long help message, " + "used to demonstrate that argparse can automatically break up long help messages " + "into multiple lines.", NULL, 0, 0), OPT_GROUP("Bits options"), OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG), OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE, 0), diff --git a/tests/basic.sh b/tests/basic.sh index c72c772c1..6c2d6eef8 100755 --- a/tests/basic.sh +++ b/tests/basic.sh @@ -54,6 +54,9 @@ Basic options -p, --path= path to read -i, --int= selected integer -s, --float= selected float + -l, --long a option that has a really really long help message, + used to demonstrate that argparse can automatically + break up long help messages into multiple lines. Bits options --read read perm