From 8d2894180252b9b469d893037232cae36d6fb5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Yves=20Landur=C3=A9?= Date: Mon, 31 Jul 2023 07:18:48 +0200 Subject: [PATCH] Add support for @option tag (partially close #51) (#64) * Add missing empty line in overview section output. * - Add @option for short option (-o) and long options (--option). - Revamp description and titles processing to normalize "\n" usage and remove empty lines at the start and end of description. - Add dt and dd rendering styles. * Add @option to README and example. * Remove useless description self-assignation. * Fix readme width after adding too long @option * Fix readme width by shorting @see example. * Fix missing < and > escaping in outputted markdown --- README.md | 60 ++++++++ examples/readme-example.md | 13 +- examples/readme-example.sh | 6 +- shdoc | 262 ++++++++++++++++++++++++++------ tests/testcases/@option.test.sh | 126 +++++++++++++++ tests/testcases/@stderr.test.sh | 1 + tests/testcases/@stdin.test.sh | 1 + tests/testcases/@stdout.test.sh | 1 + 8 files changed, 425 insertions(+), 45 deletions(-) create mode 100644 tests/testcases/@option.test.sh diff --git a/README.md b/README.md index b5b6c0d..5b9bc48 100644 --- a/README.md +++ b/README.md @@ -45,14 +45,25 @@ _Output_: [examples/readme-example.md](examples/readme-example.md)

# @example # echo "test: $(say-hello World)" # +# +# @option -h | --help Display help. +# @option -v | --value= Set a value. +# # @arg $1 string A value to print # +# @stdout Output 'Hello $1'. +# It hopes you say Hello back. +# @stderr Output 'Oups !' on error. +# It did it again. +# # @exitcode 0 If successful. # @exitcode 1 If an empty string passed. # # @see validate() +# @see [shdoc](https://github.com/reconquest/shdoc). say-hello() { if [[ ! "$1" ]]; then + echo "Oups !" >&2 return 1; fi @@ -92,6 +103,16 @@ Not thread-safe. echo "test: $(say-hello World)" ``` +#### Options + +* **-h** | **--help** + + Display help. + +* **-v\** | **--value=\** + + Set a value. + #### Arguments * **$1** (string): A value to print @@ -101,9 +122,21 @@ echo "test: $(say-hello World)" * **0**: If successful. * **1**: If an empty string passed. +#### Output on stdout + +* Output 'Hello $1'. + It hopes you say Hello back. + +#### Output on stderr + +* Output 'Oups !' on error. + It did it again. + #### See also * [validate()](#validate) +* [shdoc](https://github.com/reconquest/shdoc). + ~~~ @@ -181,12 +214,31 @@ say-hello() { } ``` +### `@option` + +A description of an option expected to be passed while calling the function. +Can be specified multiple times to describe any number of arguments. +If an option argument is expected, it must be specified between `<` and `>` + +**Example** + +```bash +# @description Says hi to a given person. +# @option -h A short option. +# @arg --value= A long option with argument. +# @arg -v | --value A long option with short option alternative. +say-hello() { + ... +} +``` + ### `@arg` A description of an argument expected to be passed while calling the function. Can be specified multiple times to describe any number of arguments. **Example** + ```bash # @description Says hi to a given person. # @arg $1 string A person's name. @@ -201,6 +253,7 @@ say-hello() { A note that the function does not expect any arguments to be passed. **Example** + ```bash # @description Says 'hello world'. # @noargs @@ -215,6 +268,7 @@ A description of a global variable that is set while calling the function. Can be specified multiple times to describe any number of variables **Example** + ```bash # @description Sets hello to the variable REPLY # @set REPLY string Greeting message. @@ -229,6 +283,7 @@ Describes an expected exitcode of the function. Can be specified multiple times to describe all possible exitcodes and their conditions. **Example** + ```bash # @description Says 'hello world'. # @exitcode 0 If successful. @@ -243,6 +298,7 @@ say-hello-world() { The expected input to the function call from `stdin` (usually the terminal or command line) **Example** + ```bash # @description Asks name. # @stdin The users name from the terminal/command line. @@ -256,6 +312,7 @@ say-hello-world() { An expected output of the function call. **Example** + ```bash # @description Says 'hello world'. # @stdout A path to a temporary file with the message. @@ -269,6 +326,7 @@ say-hello-world() { An expected output of the function call on `/dev/stderr`. **Example** + ```bash # @description Says 'hello world'. # @stderr A error message when world is not available. @@ -282,6 +340,7 @@ say-hello-world() { Create a link on the given function in the "See Also" section. **Example** + ```bash # @see say-hello # @see text with [markdown link](./other-file#other-function) @@ -298,6 +357,7 @@ It allows you to have the same style of doc comments across the script and keep functions hidden from users. **Example** + ```bash # @internal show-msg() { diff --git a/examples/readme-example.md b/examples/readme-example.md index 397f015..3f41e0a 100644 --- a/examples/readme-example.md +++ b/examples/readme-example.md @@ -25,6 +25,16 @@ Not thread-safe. echo "test: $(say-hello World)" ``` +#### Options + +* **-h** | **--help** + + Display help. + +* **-v\** | **--value=\** + + Set a value. + #### Arguments * **$1** (string): A value to print @@ -47,4 +57,5 @@ echo "test: $(say-hello World)" #### See also * [validate()](#validate) -* Documentation generated using [shdoc](https://github.com/reconquest/shdoc). \ No newline at end of file +* [shdoc](https://github.com/reconquest/shdoc). + diff --git a/examples/readme-example.sh b/examples/readme-example.sh index b6bd512..f5a3bab 100644 --- a/examples/readme-example.sh +++ b/examples/readme-example.sh @@ -14,6 +14,10 @@ # @example # echo "test: $(say-hello World)" # +# +# @option -h | --help Display help. +# @option -v | --value= Set a value. +# # @arg $1 string A value to print # # @stdout Output 'Hello $1'. @@ -25,7 +29,7 @@ # @exitcode 1 If an empty string passed. # # @see validate() -# @see Documentation generated using [shdoc](https://github.com/reconquest/shdoc). +# @see [shdoc](https://github.com/reconquest/shdoc). say-hello() { if [[ ! "$1" ]]; then echo "Oups !" >&2 diff --git a/shdoc b/shdoc index 13ae5b9..3300061 100755 --- a/shdoc +++ b/shdoc @@ -7,16 +7,19 @@ BEGIN { } styles["github", "h1", "from"] = ".*" - styles["github", "h1", "to"] = "# &" + styles["github", "h1", "to"] = "# &\n" styles["github", "h2", "from"] = ".*" - styles["github", "h2", "to"] = "## &" + styles["github", "h2", "to"] = "## &\n" styles["github", "h3", "from"] = ".*" - styles["github", "h3", "to"] = "### &" + styles["github", "h3", "to"] = "### &\n" styles["github", "h4", "from"] = ".*" - styles["github", "h4", "to"] = "#### &" + styles["github", "h4", "to"] = "#### &\n" + + styles["github", "strong", "from"] = ".*" + styles["github", "strong", "to"] = "**&**" styles["github", "code", "from"] = ".*" styles["github", "code", "to"] = "```&" @@ -35,6 +38,12 @@ BEGIN { styles["github", "li", "from"] = ".*" styles["github", "li", "to"] = "* &" + styles["github", "dt", "from"] = ".*" + styles["github", "dt", "to"] = "* &\n" + + styles["github", "dd", "from"] = "^.*$" + styles["github", "dd", "to"] = " &" + styles["github", "i", "from"] = ".*" styles["github", "i", "to"] = "_&_" @@ -175,19 +184,25 @@ function reset() { function handle_description() { debug("→ handle_description") + + # Remove empty lines at the start of description. + sub(/^[[:space:]\n]*\n/, "", description) + # Remove empty lines at the end of description. + sub(/[[:space:]\n]*$/, "", description) + if (description == "") { debug("→ → description: empty") return; } if (section != "" && section_description == "") { - debug("→ → description: added") + debug("→ → section description: added") section_description = description return; } if (file_description == "") { - debug("→ → description: added") + debug("→ → file description: added") file_description = description return; } @@ -204,7 +219,7 @@ function concat(x, text) { } function push(arr, value) { - arr[length(arr)+1] = value + arr[length(arr)] = value } function join(arr) { @@ -220,7 +235,7 @@ function join(arr) { } # @description Remove leading and trailing space from line(s) of text. -# @arg text A text. +# @param text A text. # @return The trimmed text. function trim(text) { gsub(/(^[[:blank:]]+|[[:blank:]]+$)/, "", text) @@ -239,11 +254,33 @@ function docblock_concat(key, value) { } } +# @description Add a value as the new last item of a docblock. +# +# @param docblock_name Name of the modified docblock. +# @param value Added item value. +# +# @set docblock[docblock_name] docblock with value added as its last item. function docblock_push(key, value) { - docblock[key][length(docblock[key])+1] = value + new_item_index = length(docblock[key]) + # Reinitialize docblock key value if it is empty to allow for array storage. + if(new_item_index == 0) + { + delete docblock[key] + } + if(isarray(value)) + { + + # Value is an array. Add its contents key by key to the docblock. + # Note that is only allow for single dimension value array. + for (i in value) { + docblock[key][new_item_index][i] = value[i] + } + } + else { + docblock[key][new_item_index] = value + } } - # @description Append a text to the last item of a docblock. # # @param docblock_name Name of the modified docblock. @@ -252,7 +289,13 @@ function docblock_push(key, value) { # @set docblock[docblock_name] docblock with text appended to last item. function docblock_append(docblock_name, text) { # Detect last docblock item index. - last_item_index = length(docblock[docblock_name]) + last_item_index = length(docblock[docblock_name]) - 1 + + # Ensure there is no issue if docblock[docblock_name] is empty. + if(last_item_index < 0) { + last_item_index = 0 + } + # Append text to last docblock item. docblock[docblock_name][last_item_index] = docblock[docblock_name][last_item_index] text } @@ -265,11 +308,12 @@ function docblock_append(docblock_name, text) { # # @stdout A unordered list of the dockblock entries. function render_docblock_list(docblock, docblock_name, title) { - push(lines, render("h4", title) "\n") + push(lines, render("h4", title)) # Initialize list item. item = "" # For each dockblock line. for (i in docblock[docblock_name]) { + docblock[docblock_name][i] # Ident additionnal lines to add them to the markdown list item. gsub(/\n/, "\n ", docblock[docblock_name][i]) item = render("li", docblock[docblock_name][i]) @@ -280,38 +324,148 @@ function render_docblock_list(docblock, docblock_name, title) { push(lines, "") } +# @description Process a text as an @option tag content. +# The @option accept the folling format: +# +# # @option