diff --git a/include/zephyr/shell/shell.h b/include/zephyr/shell/shell.h index 5115e35339b..92c3091b6cd 100644 --- a/include/zephyr/shell/shell.h +++ b/include/zephyr/shell/shell.h @@ -234,6 +234,82 @@ struct shell_static_entry { uint8_t padding[Z_SHELL_STATIC_ENTRY_PADDING]; }; +/** + * @brief Shell structured help descriptor. + * + * @details This structure provides an organized way to specify command help + * as opposed to a free-form string. This helps make help messages more + * consistent and easier to read. + */ +struct shell_cmd_help { + /* @cond INTERNAL_HIDDEN */ + uint32_t magic; + /* @endcond */ + const char *description; /*!< Command description */ + const char *usage; /*!< Command usage string */ +}; + +/** + * @cond INTERNAL_HIDDEN + */ + +/** + * @brief Magic number used to identify the beginning of a structured help + * message when cast to a char pointer. + */ +#define SHELL_STRUCTURED_HELP_MAGIC 0x86D20BC4 + +/** + * @endcond + */ + +/** + * @brief Check if help string is structured help. + * + * @param help Pointer to help string or structured help. + * @return true if help is structured, false otherwise. + */ +static inline bool shell_help_is_structured(const char *help) +{ + const struct shell_cmd_help *structured = (const struct shell_cmd_help *)help; + + return structured != NULL && structured->magic == SHELL_STRUCTURED_HELP_MAGIC; +} + +#if defined(CONFIG_SHELL_HELP) || defined(__DOXYGEN__) +/** + * @brief Helper macro to create structured help inline. + * + * This macro allows you to pass structured help directly to existing shell macros. + * + * Example: + * + * @code{.c} + * #define MY_CMD_HELP SHELL_HELP("Do stuff", " []") + * SHELL_CMD_REGISTER(my_cmd, NULL, MY_CMD_HELP, &my_cmd_handler, 1, 1); + * @endcode + * + * @param[in] _description Command description. + * This can be a multi-line string. First line should be one sentence + * describing the command. Additional lines might be used to provide + * additional details. + * + * @param[in] _usage Command usage string. + * This can be a multi-line string. First line should always be + * indicating the command syntax (_without_ the command name). + * Additional lines may be used to provide additional details, e.g. + * explain the meaning of each argument, allowed values, etc. + */ +#define SHELL_HELP(_description, _usage) \ + ((const char *)&(const struct shell_cmd_help){ \ + .magic = SHELL_STRUCTURED_HELP_MAGIC, \ + .description = (_description), \ + .usage = (_usage), \ + }) +#else +#define SHELL_HELP(_description, _usage) NULL +#endif /* CONFIG_SHELL_HELP */ + /** * @brief Macro for defining and adding a root command (level 0) with required * number of arguments. @@ -244,7 +320,7 @@ struct shell_static_entry { * * @param[in] syntax Command syntax (for example: history). * @param[in] subcmd Pointer to a subcommands array. - * @param[in] help Pointer to a command help string. + * @param[in] help Pointer to a command help string (use @ref SHELL_HELP for structured help) * @param[in] handler Pointer to a function handler. * @param[in] mandatory Number of mandatory arguments including command name. * @param[in] optional Number of optional arguments. @@ -275,7 +351,7 @@ struct shell_static_entry { * exists and equals 1. * @param[in] syntax Command syntax (for example: history). * @param[in] subcmd Pointer to a subcommands array. - * @param[in] help Pointer to a command help string. + * @param[in] help Pointer to a command help string (use @ref SHELL_HELP for structured help). * @param[in] handler Pointer to a function handler. * @param[in] mandatory Number of mandatory arguments including command name. * @param[in] optional Number of optional arguments. @@ -303,7 +379,7 @@ struct shell_static_entry { * * @param[in] syntax Command syntax (for example: history). * @param[in] subcmd Pointer to a subcommands array. - * @param[in] help Pointer to a command help string. + * @param[in] help Pointer to a command help string. Use @ref SHELL_HELP for structured help. * @param[in] handler Pointer to a function handler. */ #define SHELL_CMD_REGISTER(syntax, subcmd, help, handler) \ @@ -319,7 +395,7 @@ struct shell_static_entry { * exists and equals 1. * @param[in] syntax Command syntax (for example: history). * @param[in] subcmd Pointer to a subcommands array. - * @param[in] help Pointer to a command help string. + * @param[in] help Pointer to a command help string. Use @ref SHELL_HELP for structured help. * @param[in] handler Pointer to a function handler. */ #define SHELL_COND_CMD_REGISTER(flag, syntax, subcmd, help, handler) \ @@ -455,7 +531,7 @@ struct shell_static_entry { * * @param[in] syntax Command syntax (for example: history). * @param[in] subcmd Pointer to a subcommands array. - * @param[in] help Pointer to a command help string. + * @param[in] help Pointer to a command help string. Use @ref SHELL_HELP for structured help. * @param[in] handler Pointer to a function handler. * @param[in] mand Number of mandatory arguments including command name. * @param[in] opt Number of optional arguments. @@ -477,7 +553,7 @@ struct shell_static_entry { * exists and equals 1. * @param[in] syntax Command syntax (for example: history). * @param[in] subcmd Pointer to a subcommands array. - * @param[in] help Pointer to a command help string. + * @param[in] help Pointer to a command help string. Use @ref SHELL_HELP for structured help. * @param[in] handler Pointer to a function handler. * @param[in] mand Number of mandatory arguments including command name. * @param[in] opt Number of optional arguments. diff --git a/subsys/shell/shell_help.c b/subsys/shell/shell_help.c index 893cb208d84..b8d01e7913e 100644 --- a/subsys/shell/shell_help.c +++ b/subsys/shell/shell_help.c @@ -113,6 +113,22 @@ static void formatted_text_print(const struct shell *sh, const char *str, z_cursor_next_line_move(sh); } +static void formatted_structured_help_print(const struct shell *sh, const char *item_name, + const char *item_help, size_t terminal_offset) +{ + const struct shell_cmd_help *structured = (const struct shell_cmd_help *)item_help; + + if (structured->description) { + formatted_text_print(sh, structured->description, terminal_offset, false); + } + + if (structured->usage) { + z_shell_op_cursor_horiz_move(sh, terminal_offset); + z_shell_fprintf(sh, SHELL_NORMAL, "Usage: %s ", item_name); + formatted_text_print(sh, structured->usage, terminal_offset + 7, false); + } +} + static void help_item_print(const struct shell *sh, const char *item_name, uint16_t item_name_width, const char *item_help) { @@ -150,7 +166,11 @@ static void help_item_print(const struct shell *sh, const char *item_name, z_shell_fprintf(sh, SHELL_NORMAL, "%s: ", tabulator); } /* print option help */ - formatted_text_print(sh, item_help, offset, false); + if (shell_help_is_structured(item_help)) { + formatted_structured_help_print(sh, item_name, item_help, offset); + } else { + formatted_text_print(sh, item_help, offset, false); + } } /* Function prints all subcommands of the parent command together with their @@ -197,7 +217,11 @@ void z_shell_help_cmd_print(const struct shell *sh, z_shell_fprintf(sh, SHELL_NORMAL, "%s%s", cmd->syntax, cmd_sep); - formatted_text_print(sh, cmd->help, field_width, false); + if (shell_help_is_structured(cmd->help)) { + formatted_structured_help_print(sh, cmd->syntax, cmd->help, field_width); + } else { + formatted_text_print(sh, cmd->help, field_width, false); + } } bool z_shell_help_request(const char *str)