From 12c379229afe1ef5548acb487639ca71112cb4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-Yves=20Landur=C3=A9?= Date: Mon, 31 Jul 2023 07:19:41 +0200 Subject: [PATCH] Allow function bracket to be on next line, fix #56 (#65) * Allow function bracket to be on next line, fix #56 * - Fix function name extraction for `function name {}`. (function declared with function keyword, but without parenthesis. - Add function declaraction unit tests. --- shdoc | 113 +++++++++++++++--- tests/testcases/@function-declaration.test.sh | 103 ++++++++++++++++ ...n-opening-bracket-on-separate-line.test.sh | 35 ++++++ 3 files changed, 233 insertions(+), 18 deletions(-) create mode 100644 tests/testcases/@function-declaration.test.sh create mode 100644 tests/testcases/issue-56-function-opening-bracket-on-separate-line.test.sh diff --git a/shdoc b/shdoc index 3300061..013da72 100755 --- a/shdoc +++ b/shdoc @@ -84,6 +84,59 @@ function warn(message) { warn_message_color, NR, message, color_clear) > "/dev/stderr" } +# @description Process a line of text as a function declaration. +# This is a function called when encountering a function declaration, +# or a line starting by a opening bracket `{` with a previous line matching +# a function declaration. +# +# @param text The line containing the function declaration, +# with or without opening bracket. +# +# @set is_internal Set internal to 0. +# @set func_name Set the function name. +# @set doc Add function documentation to doc output. +# @set toc Add link to function documentation to table of contents. +function process_function(text) { + if ( \ + (length(docblock) == 0 && description == "") \ + || in_example \ + ) { + # If docblock and description are empty, + # or if function in example section, + # skip function declaration. + return + } + + debug("→ function") + if (is_internal) { + debug("→ → function: it is internal, skip") + is_internal = 0 + } else { + debug("→ → function: register") + + is_internal = 0 + + func_name = gensub(\ + /^[[:blank:]]*(function([[:blank:]])+)?([a-zA-Z0-9_\-:-\\.]+)[[:blank:]]*.*/, \ + "\\3", \ + "g", \ + text \ + ) + + # Add function documentation to output. + doc = concat(doc, render_docblock(func_name, description, docblock)) + # Add function link to table of contents. + toc = concat(toc, render_toc_item(func_name)) + } + + # Function document has been added to output. + # Reset variables to allow for another function documentation processing. + reset() + + # Process next line. + next +} + function render(type, text) { return gensub( \ styles[style, type, "from"], @@ -775,37 +828,61 @@ match($0, /^([[:blank:]]*#[[:blank:]]+)@(stdin|stdout|stderr)[[:blank:]]+(.*[^[: next } -/^[ \t]*(function([ \t])+)?([a-zA-Z0-9_\-:-\\.]+)([ \t]*)(\(([ \t]*)\))?[ \t]*\{/ \ - && (length(docblock) != 0 || description != "") && !in_example { - debug("→ function") - if (is_internal) { - debug("→ → function: it is internal, skip") - is_internal = 0 - } else { - debug("→ → function: register") +# If docblock if not empty, and description is set, +# and if this is not an example, +# this regex matches: +# - `function function_name () {` +# - `function_name () {` +# - `function_name {` +/^[[:blank:]]*(function[[:blank:]]+)?([a-zA-Z0-9_\-:-\\.]+)[[:blank:]]*(\([[:blank:]]*\))?[[:blank:]]*\{/ \ +{ + process_function($0) +} - is_internal = 0 - func_name = gensub(\ - /^[ \t]*(function([ \t])+)?([a-zA-Z0-9_\-:-\\.]+)[ \t]*\(.*/, \ - "\\3", \ - "g" \ - ) +# If line look like a function declaration but is missing opening bracket, +/^[[:blank:]]*(function[[:blank:]]+)?([a-zA-Z0-9_\-:-\\.]+)[[:blank:]]*(\([[:blank:]]*\))?/ \ +{ + # store it for future use + debug("→ look like a function declaration, store line") + function_declaration = $0 + next +} - doc = concat(doc, render_docblock(func_name, description, docblock)) - toc = concat(toc, render_toc_item(func_name)) - } +# Handle lone opening bracket if previous line is a function declaration. +/^[[:blank:]]*\{/ \ + && function_declaration != "" { + debug("→ multi-line function declaration.") + # Process function declaration. + process_function(function_declaration) +} - reset() +# Skip empty lines (allow for break in comment), +# if function_declaration is not empty (i.e. waiting for an opening bracket). +/^[[:blank:]]*$/ \ + && function_declaration != "" { + debug("→ waiting for opening bracket.") next } +# Handle non comment lines. /^[^#]*$/ { debug("→ break") + + # Line is not an opening bracket, + # this is not a function declaration. + function_declaration = "" + + # Add current (section) description to output. handle_description(); + + # Reset docblock. reset() + + # Skip current line. next } +# Handle everything else. This should never occur. { debug("→ NOT HANDLED") } diff --git a/tests/testcases/@function-declaration.test.sh b/tests/testcases/@function-declaration.test.sh new file mode 100644 index 0000000..201f33a --- /dev/null +++ b/tests/testcases/@function-declaration.test.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +tests:put input <