瀏覽代碼

LibCore: Enable emitting markdown from ArgsParser

ArgsParser will now automatically look for an environment variable
'ARGSPARSER_EMIT_MARKDOWN', and if it is set to exactly the string "1"
(i.e. mere presence or values like "ON" or "yes" are not enough), then
ArgsParser will emit a Markdown-formatted help message, instead of the
regular help message designed for consumption through a terminal.
Ben Wiederhake 3 年之前
父節點
當前提交
0372f051e9
共有 2 個文件被更改,包括 85 次插入0 次删除
  1. 83 0
      Userland/Libraries/LibCore/ArgsParser.cpp
  2. 2 0
      Userland/Libraries/LibCore/ArgsParser.h

+ 83 - 0
Userland/Libraries/LibCore/ArgsParser.cpp

@@ -173,6 +173,16 @@ bool ArgsParser::parse(int argc, char* const* argv, FailureBehavior failure_beha
 }
 
 void ArgsParser::print_usage(FILE* file, const char* argv0)
+{
+    char const* env_preference = getenv("ARGSPARSER_EMIT_MARKDOWN");
+    if (env_preference != nullptr && env_preference[0] == '1' && env_preference[1] == 0) {
+        print_usage_markdown(file, argv0);
+    } else {
+        print_usage_terminal(file, argv0);
+    }
+}
+
+void ArgsParser::print_usage_terminal(FILE* file, const char* argv0)
 {
     out(file, "Usage:\n\t\033[1m{}\033[0m", argv0);
 
@@ -243,6 +253,79 @@ void ArgsParser::print_usage(FILE* file, const char* argv0)
     }
 }
 
+void ArgsParser::print_usage_markdown(FILE* file, const char* argv0)
+{
+    outln(file, "## Name\n\n{}", argv0);
+
+    out(file, "\n## Synopsis\n\n```sh\n$ {}", argv0);
+    for (auto& opt : m_options) {
+        if (opt.long_name != nullptr && (!strcmp(opt.long_name, "help") || !strcmp(opt.long_name, "version")))
+            continue;
+        if (opt.requires_argument)
+            out(file, " [{} {}]", opt.name_for_display(), opt.value_name);
+        else
+            out(file, " [{}]", opt.name_for_display());
+    }
+    for (auto& arg : m_positional_args) {
+        bool required = arg.min_values > 0;
+        bool repeated = arg.max_values > 1;
+
+        if (required && repeated)
+            out(file, " <{}...>", arg.name);
+        else if (required && !repeated)
+            out(file, " <{}>", arg.name);
+        else if (!required && repeated)
+            out(file, " [{}...]", arg.name);
+        else if (!required && !repeated)
+            out(file, " [{}]", arg.name);
+    }
+    outln(file, "\n```");
+
+    if (m_general_help != nullptr && m_general_help[0] != '\0') {
+        outln(file, "\n## Description\n\n{}", m_general_help);
+    }
+
+    if (!m_options.is_empty())
+        outln(file, "\n## Options:\n");
+    for (auto& opt : m_options) {
+        auto print_argument = [&]() {
+            if (opt.value_name != nullptr) {
+                if (opt.requires_argument)
+                    out(file, " {}", opt.value_name);
+                else
+                    out(file, " [{}]", opt.value_name);
+            }
+        };
+        out(file, "* ");
+        if (opt.short_name != '\0') {
+            out(file, "`-{}", opt.short_name);
+            print_argument();
+            out(file, "`");
+        }
+        if (opt.short_name != '\0' && opt.long_name != nullptr)
+            out(file, ", ");
+        if (opt.long_name != nullptr) {
+            out(file, "`--{}", opt.long_name);
+            print_argument();
+            out(file, "`");
+        }
+
+        if (opt.help_string != nullptr)
+            out(file, ": {}", opt.help_string);
+        outln(file);
+    }
+
+    if (!m_positional_args.is_empty())
+        outln(file, "\n## Arguments:\n");
+
+    for (auto& arg : m_positional_args) {
+        out(file, "* `{}`", arg.name);
+        if (arg.help_string != nullptr)
+            out(file, ": {}", arg.help_string);
+        outln(file);
+    }
+}
+
 void ArgsParser::print_version(FILE* file)
 {
     outln(file, Core::Version::SERENITY_VERSION);

+ 2 - 0
Userland/Libraries/LibCore/ArgsParser.h

@@ -58,6 +58,8 @@ public:
     void set_general_help(const char* help_string) { m_general_help = help_string; };
     void set_stop_on_first_non_option(bool stop_on_first_non_option) { m_stop_on_first_non_option = stop_on_first_non_option; }
     void print_usage(FILE*, const char* argv0);
+    void print_usage_terminal(FILE*, const char* argv0);
+    void print_usage_markdown(FILE*, const char* argv0);
     void print_version(FILE*);
 
     void add_option(Option&&);