diff --git a/lib/gis/parser_dependencies.c b/lib/gis/parser_dependencies.c index 256b44bafa7..14f53732f43 100644 --- a/lib/gis/parser_dependencies.c +++ b/lib/gis/parser_dependencies.c @@ -491,6 +491,30 @@ int G__has_required_rule(void) return FALSE; } +const struct Option *G__first_required_option_from_rules(void) +{ + size_t i; + + for (i = 0; i < rules.count; i++) { + const struct rule *rule = &((const struct rule *)rules.data)[i]; + + if (rule->type == RULE_REQUIRED) { + if (rule->count < 0) + G_fatal_error( + _("Internal error: the number of options is < 0")); + size_t j; + for (j = 0; j < (unsigned int)rule->count; j++) { + void *p = rule->opts[j]; + if (is_flag(p)) + continue; + else + return (const struct Option *)p; + } + } + } + return NULL; +} + static const char *const rule_types[] = {"exclusive", "required", "requires", "requires-all", "excludes", "collective"}; diff --git a/lib/gis/parser_local_proto.h b/lib/gis/parser_local_proto.h index 8b75a3bac2b..aab43ea9f5e 100644 --- a/lib/gis/parser_local_proto.h +++ b/lib/gis/parser_local_proto.h @@ -48,12 +48,23 @@ struct state { extern struct state *st; +#define MD_NEWLINE " " + /* functions which are used by several parser functions in different files */ void G__usage_xml(void); void G__usage_html(void); void G__usage_rest(void); + void G__usage_markdown(void); +void G__md_print_cli_short_version(FILE *file, const char *indent); +void G__md_print_python_short_version(FILE *file, const char *indent); +void G__md_print_cli_long_version(FILE *file, const char *indent); +void G__md_print_python_long_version(FILE *file, const char *indent); +void G__md_print_escaped(FILE *f, const char *str); +void G__md_print_escaped_for_options(FILE *f, const char *str); +int G__option_num_tuple_items(const struct Option *opt); + void G__usage_text(void); void G__script(void); char *G__json(void); @@ -66,6 +77,7 @@ void G__split_gisprompt(const char *, char *, char *, char *); void G__check_option_rules(void); void G__describe_option_rules(void); int G__has_required_rule(void); +const struct Option *G__first_required_option_from_rules(void); void G__describe_option_rules_xml(FILE *); #endif diff --git a/lib/gis/parser_md.c b/lib/gis/parser_md.c index da10cdf59a6..0bb8b4a31a4 100644 --- a/lib/gis/parser_md.c +++ b/lib/gis/parser_md.c @@ -9,6 +9,7 @@ (>=v2). Read the file COPYING that comes with GRASS for details. \author Martin Landa + \author Vaclav Petras */ #include #include @@ -18,27 +19,11 @@ #include "parser_local_proto.h" -#define MD_NEWLINE " " - -static void print_flag(const char *key, const char *label, - const char *description); -void print_option(const struct Option *opt); -static void print_escaped(FILE *f, const char *str); -static void print_escaped_for_md(FILE *f, const char *str); -static void print_escaped_for_md_options(FILE *f, const char *str); - /*! \brief Print module usage description in Markdown format. */ void G__usage_markdown(void) { - struct Option *opt; - struct Flag *flag; - const char *type; - int new_prompt = 0; - - new_prompt = G__uses_new_gisprompt(); - if (!st->pgm_name) st->pgm_name = G_program_name(); if (!st->pgm_name) @@ -56,15 +41,7 @@ void G__usage_markdown(void) /* main header */ fprintf(stdout, "# %s\n\n", st->pgm_name); - /* header - GRASS module */ - fprintf(stdout, "## "); - fprintf(stdout, "%s\n", _("NAME")); - fprintf(stdout, "\n"); - fprintf(stdout, "***%s***", st->pgm_name); - - if (st->module_info.label || st->module_info.description) - fprintf(stdout, " - "); - + /* header */ if (st->module_info.label) fprintf(stdout, "%s\n", st->module_info.label); @@ -73,295 +50,20 @@ void G__usage_markdown(void) fprintf(stdout, "\n"); fprintf(stdout, "%s\n", st->module_info.description); } - fprintf(stdout, "\n"); - fprintf(stdout, "### "); - fprintf(stdout, "\n"); - fprintf(stdout, "### "); - fprintf(stdout, "%s\n", _("SYNOPSIS")); - fprintf(stdout, "\n"); - fprintf(stdout, "**%s**", st->pgm_name); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - fprintf(stdout, "**%s --help**", st->pgm_name); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - fprintf(stdout, "**%s**", st->pgm_name); - - /* print short version first */ - if (st->n_flags) { - flag = &st->first_flag; - fprintf(stdout, " [**-"); - while (flag != NULL) { - fprintf(stdout, "%c", flag->key); - flag = flag->next_flag; - } - fprintf(stdout, "**] "); - } - else - fprintf(stdout, " "); - - if (st->n_opts) { - opt = &st->first_option; - - while (opt != NULL) { - if (opt->key_desc != NULL) - type = opt->key_desc; - else - switch (opt->type) { - case TYPE_INTEGER: - type = "integer"; - break; - case TYPE_DOUBLE: - type = "float"; - break; - case TYPE_STRING: - type = "string"; - break; - default: - type = "string"; - break; - } - fprintf(stdout, " "); - if (!opt->required) - fprintf(stdout, "["); - fprintf(stdout, "**%s**=", opt->key); - fprintf(stdout, "*%s*", type); - if (opt->multiple) { - fprintf(stdout, " [,"); - fprintf(stdout, "*%s*,...]", type); - } - if (!opt->required) - fprintf(stdout, "]"); - fprintf(stdout, "\n"); - - opt = opt->next_opt; - } - } - if (new_prompt) - fprintf(stdout, " [**--overwrite**] "); - - fprintf(stdout, " [**--verbose**] "); - fprintf(stdout, " [**--quiet**] "); - fprintf(stdout, " [**--ui**]\n"); - - /* now long version */ - fprintf(stdout, "\n"); - if (st->n_flags || new_prompt) { - flag = &st->first_flag; - fprintf(stdout, "#### "); - fprintf(stdout, "%s\n", _("Flags")); - fprintf(stdout, "\n"); - while (st->n_flags && flag != NULL) { - print_flag(&flag->key, flag->label, flag->description); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - flag = flag->next_flag; - } - if (new_prompt) { - print_flag("overwrite", NULL, - _("Allow output files to overwrite existing files")); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - } - } - print_flag("help", NULL, _("Print usage summary")); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - print_flag("verbose", NULL, _("Verbose module output")); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - print_flag("quiet", NULL, _("Quiet module output")); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - print_flag("ui", NULL, _("Force launching GUI dialog")); - fprintf(stdout, "\n"); - - if (st->n_opts) { - fprintf(stdout, "\n"); - opt = &st->first_option; - fprintf(stdout, "#### "); - fprintf(stdout, "%s\n", _("Parameters")); - fprintf(stdout, "\n"); - while (opt != NULL) { - print_option(opt); - opt = opt->next_opt; - if (opt != NULL) { - fprintf(stdout, MD_NEWLINE); - } - fprintf(stdout, "\n"); - } - } -} - -void print_flag(const char *key, const char *label, const char *description) -{ - fprintf(stdout, "**"); - if (strlen(key) > 1) - fprintf(stdout, "-"); - fprintf(stdout, "-%s**", key); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - if (label != NULL) { - print_escaped(stdout, "\t"); - print_escaped(stdout, label); - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - } - if (description != NULL) { - print_escaped(stdout, "\t"); - print_escaped(stdout, description); - } -} - -void print_option(const struct Option *opt) -{ - const char *type; - - /* TODO: make this a enumeration type? */ - if (opt->key_desc != NULL) - type = opt->key_desc; - else - switch (opt->type) { - case TYPE_INTEGER: - type = "integer"; - break; - case TYPE_DOUBLE: - type = "float"; - break; - case TYPE_STRING: - type = "string"; - break; - default: - type = "string"; - break; - } - fprintf(stdout, "**%s**=", opt->key); - fprintf(stdout, "*%s*", type); - if (opt->multiple) { - fprintf(stdout, " [,"); - fprintf(stdout, "*%s*,...]", type); - } - /* fprintf(stdout, "*"); */ - if (opt->required) { - fprintf(stdout, " **[required]**"); - } - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - if (opt->label) { - print_escaped(stdout, "\t"); - print_escaped(stdout, opt->label); - } - if (opt->description) { - if (opt->label) { - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - } - print_escaped(stdout, "\t"); - print_escaped(stdout, opt->description); - } - - if (opt->options) { - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - print_escaped(stdout, "\t"); - fprintf(stdout, "%s: *", _("Options")); - print_escaped_for_md_options(stdout, opt->options); - fprintf(stdout, "*"); - } - - if (opt->def) { - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - print_escaped(stdout, "\t"); - fprintf(stdout, "%s:", _("Default")); - /* TODO check if value is empty - if (!opt->def.empty()){ */ - fprintf(stdout, " *"); - print_escaped(stdout, opt->def); - fprintf(stdout, "*"); - } - - if (opt->descs) { - int i = 0; - - while (opt->opts[i]) { - if (opt->descs[i]) { - fprintf(stdout, MD_NEWLINE); - fprintf(stdout, "\n"); - char *thumbnails = NULL; - if (opt->gisprompt) { - if (strcmp(opt->gisprompt, "old,colortable,colortable") == - 0) - thumbnails = "colortables"; - else if (strcmp(opt->gisprompt, "old,barscale,barscale") == - 0) - thumbnails = "barscales"; - else if (strcmp(opt->gisprompt, - "old,northarrow,northarrow") == 0) - thumbnails = "northarrows"; - - if (thumbnails) { - print_escaped(stdout, "\t\t"); - fprintf(stdout, "![%s](%s/%s.png) ", opt->opts[i], - thumbnails, opt->opts[i]); - } - else { - print_escaped(stdout, "\t\t"); - } - } - print_escaped(stdout, "\t"); - fprintf(stdout, "**"); - print_escaped(stdout, opt->opts[i]); - fprintf(stdout, "**: "); - print_escaped(stdout, opt->descs[i]); - } - i++; - } - } -} - -/*! - * \brief Format text for Markdown output - */ -#define do_escape(c, escaped) \ - case c: \ - fputs(escaped, f); \ - break - -void print_escaped(FILE *f, const char *str) -{ - print_escaped_for_md(f, str); -} -void print_escaped_for_md(FILE *f, const char *str) -{ - const char *s; + const char *tab_indent = " "; - for (s = str; *s; s++) { - switch (*s) { - do_escape('\n', "\\\n"); - do_escape('\t', "    "); - do_escape('<', "<"); - do_escape('>', ">"); - do_escape('*', "\\*"); - default: - fputc(*s, f); - } - } -} + /* short version */ + fprintf(stdout, "\n=== \"Command line\"\n\n"); + G__md_print_cli_short_version(stdout, tab_indent); + fprintf(stdout, "\n=== \"Python (grass.script)\"\n\n"); + G__md_print_python_short_version(stdout, tab_indent); -void print_escaped_for_md_options(FILE *f, const char *str) -{ - const char *s; + fprintf(stdout, "\n## %s\n", _("Parameters")); - for (s = str; *s; s++) { - switch (*s) { - do_escape('\n', "\n\n"); - do_escape(',', ", "); - default: - fputc(*s, f); - } - } + /* long version */ + fprintf(stdout, "\n=== \"Command line\"\n\n"); + G__md_print_cli_long_version(stdout, tab_indent); + fprintf(stdout, "\n=== \"Python (grass.script)\"\n\n"); + G__md_print_python_long_version(stdout, tab_indent); } - -#undef do_escape diff --git a/lib/gis/parser_md_cli.c b/lib/gis/parser_md_cli.c new file mode 100644 index 00000000000..3f4968855c6 --- /dev/null +++ b/lib/gis/parser_md_cli.c @@ -0,0 +1,334 @@ +/*! + \file lib/gis/parser_md_cli.c + + \brief GIS Library - Argument parsing functions (Markdown output - CLI) + + (C) 2025 by the GRASS Development Team + + This program is free software under the GNU General Public License + (>=v2). Read the file COPYING that comes with GRASS for details. + + \author Vaclav Petras + */ +#include +#include + +#include +#include + +#include "parser_local_proto.h" + +static void print_cli_flag(FILE *file, const char *key, const char *label, + const char *description, const char *indent); +static void print_cli_option(FILE *file, const struct Option *opt, + const char *indent); +static void print_cli_example(FILE *file, const char *indent); + +void print_cli_flag(FILE *file, const char *key, const char *label, + const char *description, const char *indent) +{ + fprintf(file, "%s**", indent); + if (strlen(key) > 1) + fprintf(file, "-"); + fprintf(file, "-%s**", key); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + if (label != NULL) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, label); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + if (description != NULL) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, description); + } +} + +void print_cli_option(FILE *file, const struct Option *opt, const char *indent) +{ + const char *type; + + if (opt->key_desc != NULL) + type = opt->key_desc; + else + switch (opt->type) { + case TYPE_INTEGER: + type = "integer"; + break; + case TYPE_DOUBLE: + type = "float"; + break; + case TYPE_STRING: + type = "string"; + break; + default: + type = "string"; + break; + } + fprintf(file, "%s**%s**=", indent, opt->key); + fprintf(file, "*%s*", type); + if (opt->multiple) { + fprintf(file, " [,"); + fprintf(file, "*%s*,...]", type); + } + /* fprintf(file, "*"); */ + if (opt->required) { + fprintf(file, " **[required]**"); + } + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + if (opt->label) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, opt->label); + } + if (opt->description) { + if (opt->label) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, opt->description); + } + + if (opt->options) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "%s: *", _("Allowed values")); + G__md_print_escaped_for_options(file, opt->options); + fprintf(file, "*"); + } + + if (opt->def) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "%s:", _("Default")); + /* TODO check if value is empty + if (!opt->def.empty()){ */ + fprintf(file, " *"); + G__md_print_escaped(file, opt->def); + fprintf(file, "*"); + } + + if (opt->descs) { + int i = 0; + + while (opt->opts[i]) { + if (opt->descs[i]) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + char *thumbnails = NULL; + if (opt->gisprompt) { + if (strcmp(opt->gisprompt, "old,colortable,colortable") == + 0) + thumbnails = "colortables"; + else if (strcmp(opt->gisprompt, "old,barscale,barscale") == + 0) + thumbnails = "barscales"; + else if (strcmp(opt->gisprompt, + "old,northarrow,northarrow") == 0) + thumbnails = "northarrows"; + + if (thumbnails) { + G__md_print_escaped(file, "\t\t"); + fprintf(file, "![%s](%s/%s.png) ", opt->opts[i], + thumbnails, opt->opts[i]); + } + else { + G__md_print_escaped(file, "\t\t"); + } + } + G__md_print_escaped(file, "\t"); + fprintf(file, "**"); + G__md_print_escaped(file, opt->opts[i]); + fprintf(file, "**: "); + G__md_print_escaped(file, opt->descs[i]); + } + i++; + } + } +} + +void print_cli_example(FILE *file, const char *indent) +{ + fprintf(file, "\n%sExample:\n", indent); + + fprintf(file, "\n%s```sh\n", indent); + fprintf(file, "%s%s", indent, st->pgm_name); + + const struct Option *first_required_rule_option = + G__first_required_option_from_rules(); + const struct Option *opt = NULL; + const char *type; + + if (st->n_opts) { + opt = &st->first_option; + + while (opt != NULL) { + if (opt->key_desc != NULL) + type = opt->key_desc; + else + switch (opt->type) { + case TYPE_INTEGER: + type = "integer"; + break; + case TYPE_DOUBLE: + type = "float"; + break; + case TYPE_STRING: + type = "string"; + break; + default: + type = "string"; + break; + } + if (opt->required || first_required_rule_option == opt) { + fprintf(file, " "); + fprintf(file, "%s=", opt->key); + if (opt->answer) { + fprintf(file, "%s", opt->answer); + } + else { + fprintf(file, "%s", type); + } + } + opt = opt->next_opt; + } + } + fprintf(file, "\n%s```\n", indent); +} + +void G__md_print_cli_short_version(FILE *file, const char *indent) +{ + struct Option *opt; + struct Flag *flag; + const char *type; + int new_prompt = 0; + + new_prompt = G__uses_new_gisprompt(); + + fprintf(file, "%s**%s**", indent, st->pgm_name); + fprintf(file, "\n"); + + /* print short version first */ + if (st->n_flags) { + flag = &st->first_flag; + fprintf(file, "%s[**-", indent); + while (flag != NULL) { + fprintf(file, "%c", flag->key); + flag = flag->next_flag; + } + fprintf(file, "**]"); + fprintf(file, "\n"); + } + + if (st->n_opts) { + opt = &st->first_option; + + while (opt != NULL) { + if (opt->key_desc != NULL) + type = opt->key_desc; + else + switch (opt->type) { + case TYPE_INTEGER: + type = "integer"; + break; + case TYPE_DOUBLE: + type = "float"; + break; + case TYPE_STRING: + type = "string"; + break; + default: + type = "string"; + break; + } + fprintf(file, "%s", indent); + if (!opt->required) + fprintf(file, "["); + fprintf(file, "**%s**=", opt->key); + fprintf(file, "*%s*", type); + if (opt->multiple) { + fprintf(file, " [,"); + fprintf(file, "*%s*,...]", type); + } + if (!opt->required) + fprintf(file, "]"); + fprintf(file, "\n"); + + opt = opt->next_opt; + } + } + if (new_prompt) + fprintf(file, "%s[**--overwrite**]\n", indent); + + fprintf(file, "%s[**--verbose**]\n", indent); + fprintf(file, "%s[**--quiet**]\n", indent); + fprintf(file, "%s[**--qq**]\n", indent); + fprintf(file, "%s[**--ui**]\n", indent); + + print_cli_example(file, indent); +} + +void G__md_print_cli_long_version(FILE *file, const char *indent) +{ + struct Option *opt; + struct Flag *flag; + int new_prompt = 0; + + new_prompt = G__uses_new_gisprompt(); + + // Options (key-value parameters) + if (st->n_opts) { + opt = &st->first_option; + while (opt != NULL) { + print_cli_option(file, opt, indent); + opt = opt->next_opt; + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + } + + // Short (one-letter) flags and tool-specific long flags + if (st->n_flags || new_prompt) { + flag = &st->first_flag; + while (st->n_flags && flag != NULL) { + print_cli_flag(file, &flag->key, flag->label, flag->description, + indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + flag = flag->next_flag; + } + if (new_prompt) { + print_cli_flag(file, "overwrite", NULL, + _("Allow output files to overwrite existing files"), + indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + } + // Pre-defined long flags + print_cli_flag(file, "help", NULL, _("Print usage summary"), indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + print_cli_flag(file, "verbose", NULL, _("Verbose module output"), indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + print_cli_flag(file, "quiet", NULL, _("Quiet module output"), indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + print_cli_flag(file, "qq", NULL, _("Very quiet module output"), indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + print_cli_flag(file, "ui", NULL, _("Force launching GUI dialog"), indent); + fprintf(file, "\n"); +} diff --git a/lib/gis/parser_md_common.c b/lib/gis/parser_md_common.c new file mode 100644 index 00000000000..a26c50d76c9 --- /dev/null +++ b/lib/gis/parser_md_common.c @@ -0,0 +1,94 @@ +/*! + \file lib/gis/parser_md_common.c + + \brief GIS Library - Argument parsing functions (Markdown output) + + (C) 2023-2025 by the GRASS Development Team + + This program is free software under the GNU General Public License + (>=v2). Read the file COPYING that comes with GRASS for details. + + \author Martin Landa + */ +#include +#include + +#include +#include + +#include "parser_local_proto.h" + +/*! + * \brief Format text for Markdown output + */ +#define do_escape(c, escaped) \ + case c: \ + fputs(escaped, f); \ + break + +void G__md_print_escaped(FILE *f, const char *str) +{ + const char *s; + + for (s = str; *s; s++) { + switch (*s) { + do_escape('\n', "\\\n"); + do_escape('\t', "    "); + do_escape('<', "<"); + do_escape('>', ">"); + do_escape('*', "\\*"); + default: + fputc(*s, f); + } + } +} + +void G__md_print_escaped_for_options(FILE *f, const char *str) +{ + const char *s; + + for (s = str; *s; s++) { + switch (*s) { + do_escape('\n', "\n\n"); + do_escape(',', ", "); + default: + fputc(*s, f); + } + } +} + +#undef do_escape + +// This can eventually go to a file with functions related to Option, +// but for now it is here until parser.c is refactored. +/** + * \brief Get number of tuple items if option is a tuple + * + * Note that parser code generally does not consider tuples with only one + * item, so this function never returns 1. + * + * The number of items is determined by counting commas in the option key + * description. + * + * \param opt Option definition + * \return Number of items or zero if not a tuple + */ +int G__option_num_tuple_items(const struct Option *opt) +{ + // If empty, it cannot be considered a tuple. + if (!opt->key_desc) + return 0; + + int n_items = 1; + const char *ptr; + + for (ptr = opt->key_desc; *ptr != '\0'; ptr++) + if (*ptr == ',') + n_items++; + + // Only one item is not considered a tuple. + if (n_items == 1) + return 0; + // Only two and more items are a tuple. + return n_items; +} diff --git a/lib/gis/parser_md_python.c b/lib/gis/parser_md_python.c new file mode 100644 index 00000000000..9bc80031c52 --- /dev/null +++ b/lib/gis/parser_md_python.c @@ -0,0 +1,485 @@ +/*! + \file lib/gis/parser_md_python.c + + \brief GIS Library - Argument parsing functions (Markdown output - Python) + + (C) 2025 by the GRASS Development Team + + This program is free software under the GNU General Public License + (>=v2). Read the file COPYING that comes with GRASS for details. + + \author Vaclav Petras + */ +#include +#include + +#include +#include + +#include "parser_local_proto.h" + +static void print_python_short_flag(FILE *file, const char *key, + const char *label, const char *description, + const char *indent); +static void print_python_long_flag(FILE *file, const char *key, + const char *label, const char *description, + const char *indent); +static void print_python_option(FILE *file, const struct Option *opt, + const char *indent); +static void print_python_example(FILE *file, const char *python_function, + const char *output_format_default, + const char *indent); +static void print_python_tuple(FILE *file, const char *type, int num_items); + +void print_python_short_flag(FILE *file, const char *key, const char *label, + const char *description, const char *indent) +{ + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "**%s**", key); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + if (label != NULL) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t\t"); + G__md_print_escaped(file, label); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + if (description != NULL) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t\t"); + G__md_print_escaped(file, description); + } +} + +void print_python_long_flag(FILE *file, const char *key, const char *label, + const char *description, const char *indent) +{ + fprintf(file, "%s**%s**: bool, *optional*", indent, key); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + if (label != NULL) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, label); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + if (description != NULL) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, description); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "Default: *False*"); +} + +void print_python_tuple(FILE *file, const char *type, int num_items) +{ + fprintf(file, "tuple[%s", type); + for (int i = 1; i < num_items; i++) { + fprintf(file, ", %s", type); + } + fprintf(file, "]"); +} + +void print_python_option(FILE *file, const struct Option *opt, + const char *indent) +{ + const char *type; + + switch (opt->type) { + case TYPE_INTEGER: + type = "int"; + break; + case TYPE_DOUBLE: + type = "float"; + break; + case TYPE_STRING: + type = "str"; + break; + default: + type = "str"; + break; + } + fprintf(file, "%s**%s** : ", indent, opt->key); + int tuple_items = G__option_num_tuple_items(opt); + if (opt->multiple) { + if (tuple_items) { + fprintf(file, "list["); + print_python_tuple(file, type, tuple_items); + fprintf(file, "] | "); + print_python_tuple(file, type, tuple_items); + fprintf(file, " | list[%s] | str", type); + } + else { + if (strcmp(type, "str")) { + // If it is not a string, we also show it can be a string + // because that may be more relevant to show that for + // lists due to examples (it is possible for single value as + // well). + fprintf(file, "%s | list[%s] | str", type, type); + } + else { + fprintf(file, "%s | list[%s]", type, type); + } + } + } + else if (tuple_items) { + print_python_tuple(file, type, tuple_items); + fprintf(file, " | list[%s] | str", type); + } + else { + fprintf(file, "%s", type); + } + if (opt->required) { + fprintf(file, ", *required*"); + } + else { + fprintf(file, ", *optional*"); + } + + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + if (opt->label) { + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, opt->label); + } + if (opt->description) { + if (opt->label) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + G__md_print_escaped(file, opt->description); + } + if (opt->gisprompt || opt->key_desc) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "%s: ", _("Used as")); + } + if (opt->gisprompt) { + char age[KEYLENGTH]; + char element[KEYLENGTH]; + char desc[KEYLENGTH]; + G__split_gisprompt(opt->gisprompt, age, element, desc); + if (strcmp(age, "new") == 0) + fprintf(file, "output, "); + else if (strcmp(age, "old") == 0) + fprintf(file, "input, "); + // While element more strictly expresses how the value will be + // used given that the parser may read that information, desc + // is meant as a user-facing representation of the same + // information. + fprintf(file, "%s", desc); + } + if (opt->gisprompt && opt->key_desc) { + fprintf(file, ", "); + } + if (opt->key_desc) { + fprintf(file, "*%s*", opt->key_desc); + } + + if (opt->options) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "%s: *", _("Allowed values")); + G__md_print_escaped_for_options(file, opt->options); + fprintf(file, "*"); + } + + if (opt->descs) { + int i = 0; + + while (opt->opts[i]) { + if (opt->descs[i]) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + char *thumbnails = NULL; + if (opt->gisprompt) { + if (strcmp(opt->gisprompt, "old,colortable,colortable") == + 0) + thumbnails = "colortables"; + else if (strcmp(opt->gisprompt, "old,barscale,barscale") == + 0) + thumbnails = "barscales"; + else if (strcmp(opt->gisprompt, + "old,northarrow,northarrow") == 0) + thumbnails = "northarrows"; + + if (thumbnails) { + G__md_print_escaped(file, "\t\t"); + fprintf(file, "![%s](%s/%s.png) ", opt->opts[i], + thumbnails, opt->opts[i]); + } + else { + G__md_print_escaped(file, "\t\t"); + } + } + G__md_print_escaped(file, "\t"); + fprintf(file, "**"); + G__md_print_escaped(file, opt->opts[i]); + fprintf(file, "**: "); + G__md_print_escaped(file, opt->descs[i]); + } + i++; + } + } + + if (opt->def) { + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "%s:", _("Default")); + fprintf(file, " *"); + G__md_print_escaped(file, opt->def); + fprintf(file, "*"); + } +} + +void print_python_example(FILE *file, const char *python_function, + const char *output_format_default, const char *indent) +{ + fprintf(file, "\n%sExample:\n", indent); + + fprintf(file, "\n%s```python\n", indent); + fprintf(file, "%sgs.%s(\"%s\"", indent, python_function, st->pgm_name); + + const struct Option *first_required_rule_option = + G__first_required_option_from_rules(); + const struct Option *opt = NULL; + const char *type; + + if (st->n_opts) { + opt = &st->first_option; + + while (opt != NULL) { + if (opt->key_desc != NULL) + type = opt->key_desc; + else + switch (opt->type) { + case TYPE_INTEGER: + type = "integer"; + break; + case TYPE_DOUBLE: + type = "float"; + break; + case TYPE_STRING: + type = "string"; + break; + default: + type = "string"; + break; + } + if (opt->required || first_required_rule_option == opt) { + fprintf(file, ", %s=", opt->key); + if (output_format_default && strcmp(opt->key, "format") == 0) { + fprintf(file, "\"%s\"", output_format_default); + } + else if (opt->answer) { + if (opt->type == TYPE_INTEGER || opt->type == TYPE_DOUBLE) { + fprintf(file, "%s", opt->answer); + } + else { + fprintf(file, "\"%s\"", opt->answer); + } + } + else { + if (opt->type == TYPE_INTEGER || opt->type == TYPE_DOUBLE) { + fprintf(file, "%s", type); + } + else { + fprintf(file, "\"%s\"", type); + } + } + } + opt = opt->next_opt; + } + } + fprintf(file, ")\n%s```\n", indent); +} + +void G__md_print_python_short_version(FILE *file, const char *indent) +{ + struct Option *opt; + struct Flag *flag; + int new_prompt = 0; + bool output_format_option = false; + const char *output_format_default = NULL; + bool shell_eval_flag = false; + const char *python_function = NULL; + + new_prompt = G__uses_new_gisprompt(); + + if (st->n_opts) { + opt = &st->first_option; + while (opt != NULL) { + if (strcmp(opt->key, "format") == 0) { + if (opt->options) { + int i = 0; + while (opt->opts[i]) { + if (strcmp(opt->opts[i], "csv") == 0) + output_format_default = "csv"; + if (strcmp(opt->opts[i], "json") == 0) { + output_format_default = "json"; + break; + } + i++; + } + } + if (output_format_default) { + output_format_option = true; + } + break; + } + opt = opt->next_opt; + } + } + if (st->n_flags) { + flag = &st->first_flag; + while (st->n_flags && flag != NULL) { + if (flag->key == 'g') { + shell_eval_flag = true; + break; + } + flag = flag->next_flag; + } + } + if (output_format_option || (!new_prompt && shell_eval_flag)) { + python_function = "parse_command"; + // We know this is can be parsed, but we can't detect just plain file + // because we can't distinguish between plain text outputs and + // modifications of data. + } + else { + python_function = "run_command"; + } + fprintf(file, "%s*grass.script.%s*(\"***%s***\",", indent, python_function, + st->pgm_name); + fprintf(file, "\n"); + + if (st->n_opts) { + opt = &st->first_option; + + while (opt != NULL) { + fprintf(file, "%s ", indent); + if (!opt->required && !opt->answer) { + fprintf(file, "**%s**=*None*", opt->key); + } + else { + fprintf(file, "**%s**", opt->key); + } + if (opt->answer) { + fprintf(file, "="); + int tuple_items = G__option_num_tuple_items(opt); + if (!tuple_items && + (opt->type == TYPE_INTEGER || opt->type == TYPE_DOUBLE)) { + fprintf(file, "*"); + G__md_print_escaped(file, opt->answer); + fprintf(file, "*"); + } + else { + fprintf(file, "*\""); + G__md_print_escaped(file, opt->answer); + fprintf(file, "\"*"); + } + } + fprintf(file, ",\n"); + + opt = opt->next_opt; + } + } + + if (st->n_flags) { + flag = &st->first_flag; + fprintf(file, "%s **flags**=*None*,\n", indent); + } + + if (new_prompt) + fprintf(file, "%s **overwrite**=*False*,\n", indent); + + fprintf(file, "%s **verbose**=*False*,\n", indent); + fprintf(file, "%s **quiet**=*False*,\n", indent); + fprintf(file, "%s **superquiet**=*False*)\n", indent); + + print_python_example(file, python_function, output_format_default, indent); +} + +void G__md_print_python_long_version(FILE *file, const char *indent) +{ + struct Option *opt; + struct Flag *flag; + int new_prompt = 0; + + new_prompt = G__uses_new_gisprompt(); + + // Options (key-value parameters) + if (st->n_opts) { + opt = &st->first_option; + while (opt != NULL) { + print_python_option(file, opt, indent); + opt = opt->next_opt; + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + } + + // Short (one-letter) flags and tool-specific long flags + if (st->n_flags) { + fprintf(file, "%s**flags** : str, *optional*", indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + fprintf(file, "%s", indent); + G__md_print_escaped(file, "\t"); + fprintf(file, "Allowed values: "); + flag = &st->first_flag; + while (st->n_flags && flag != NULL) { + fprintf(file, "*%s*", &flag->key); + flag = flag->next_flag; + if (flag != NULL) + fprintf(file, ", "); + } + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + flag = &st->first_flag; + while (st->n_flags && flag != NULL) { + print_python_short_flag(file, &flag->key, flag->label, + flag->description, indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + flag = flag->next_flag; + } + } + if (new_prompt) { + print_python_long_flag( + file, "overwrite", NULL, + _("Allow output files to overwrite existing files"), indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + } + // Pre-defined long flags + print_python_long_flag(file, "verbose", NULL, _("Verbose module output"), + indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + print_python_long_flag(file, "quiet", NULL, _("Quiet module output"), + indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); + print_python_long_flag(file, "superquiet", NULL, + _("Very quiet module output"), indent); + fprintf(file, MD_NEWLINE); + fprintf(file, "\n"); +} diff --git a/man/mkdocs/mkdocs.yml b/man/mkdocs/mkdocs.yml index 09d9864ba8e..3d32b48b7bd 100644 --- a/man/mkdocs/mkdocs.yml +++ b/man/mkdocs/mkdocs.yml @@ -98,7 +98,6 @@ markdown_extensions: - pymdownx.superfences - pymdownx.tasklist - pymdownx.snippets - - pymdownx.tabbed - pymdownx.magiclink - attr_list - md_in_html