shell: Introduce structured help message

In order to enable better and more consistent help messages for shell
commands, this commit introduces a new SHELL_HELP macro that allows to
document a command's description and usage in a more structured way.

This will allow to get rid of inconsistent (and duplicated!) variations
of "Syntax: ...", "Syntax:\n ...", "Usage: ...", in the commands, and
formatting of the description+usage will be taken care of by the shell
when it detects the help message is using the "structured" format.

Signed-off-by: Benjamin Cabé <benjamin@zephyrproject.org>
This commit is contained in:
Benjamin Cabé 2025-06-04 23:02:52 +02:00 committed by Benjamin Cabé
commit 9ab0ea73a8
2 changed files with 108 additions and 8 deletions

View file

@ -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", "<device> <arg1> [<arg2>]")
* 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.

View file

@ -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)