Skip to content

Commit

Permalink
- Allow for indented function description (fix reconquest#50).
Browse files Browse the repository at this point in the history
- Add common code for multiline support for:
  description, example, stdout, stderr, stdin, set, see, and exitcode.
  • Loading branch information
landure committed Nov 12, 2022
1 parent 7ff4317 commit 9cfcde9
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 150 deletions.
267 changes: 133 additions & 134 deletions shdoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ BEGIN {
styles["github", "set", "from"] = "^(\\S+) (\\S+)"
styles["github", "set", "to"] = "**\\1** (\\2):"

styles["github", "li-preprocess", "from"] = "\n"
styles["github", "li-preprocess", "to"] = "\n "

styles["github", "li", "from"] = ".*"
styles["github", "li", "to"] = "* &"

Expand All @@ -50,7 +53,7 @@ BEGIN {
styles["github", "anchor", "from"] = ".*"
styles["github", "anchor", "to"] = "[&](#&)"

styles["github", "exitcode", "from"] = "([>!]?[0-9]{1,3}) (.*)"
styles["github", "exitcode", "from"] = "([>!]?[0-9]{1,3})[[:blank:]](.*)"
styles["github", "exitcode", "to"] = "**\\1**: \\2"

stderr_section_flag = 0
Expand Down Expand Up @@ -182,28 +185,30 @@ function reset() {
description = ""
}

function handle_description() {
function handle_description(text) {
debug("→ handle_description")

# Remove empty lines at the start of description.
sub(/^[[:space:]\n]*\n/, "", description)
sub(/^[[:space:]\n]*\n/, "", text)
# Remove empty lines at the end of description.
sub(/[[:space:]\n]*$/, "", description)
sub(/[[:space:]\n]*$/, "", text)

if (description == "") {
if (text == "") {
debug("→ → description: empty")
return;
}

description = text

if (section != "" && section_description == "") {
debug("→ → section description: added")
section_description = description
section_description = text
return;
}

if (file_description == "") {
debug("→ → file description: added")
file_description = description
file_description = text
return;
}
}
Expand Down Expand Up @@ -315,8 +320,8 @@ function render_docblock_list(docblock, docblock_name, title) {
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])
item = render("li-preprocess", docblock[docblock_name][i])
item = render("li", item)
push(lines, item)
}

Expand Down Expand Up @@ -428,7 +433,9 @@ function render_docblock(func_name, description, docblock) {
if ("example" in docblock) {
push(lines, render("h4", "Example"))
push(lines, render("code", "bash"))
push(lines, unindent(docblock["example"]))
# Unindent should be done by the new code.
#push(lines, unindent(docblock["example"]))
push(lines, docblock["example"])
push(lines, render("/code"))
push(lines, "")
}
Expand Down Expand Up @@ -493,8 +500,8 @@ function render_docblock(func_name, description, docblock) {
if ("set" in docblock) {
push(lines, render("h4", "Variables set"))
for (i in docblock["set"]) {
item = docblock["set"][i]
item = render("set", item)
item = render("set", docblock["set"][i])
item = render("li-preprocess", item)
item = render("li", item)
push(lines, item)
}
Expand All @@ -506,7 +513,9 @@ function render_docblock(func_name, description, docblock) {
if ("exitcode" in docblock) {
push(lines, render("h4", "Exit codes"))
for (i in docblock["exitcode"]) {
item = render("li", render("exitcode", docblock["exitcode"][i]))
item = render("exitcode", docblock["exitcode"][i])
item = render("li-preprocess", item)
item = render("li", item)
push(lines, item)
}

Expand All @@ -529,7 +538,8 @@ function render_docblock(func_name, description, docblock) {
if ("see" in docblock) {
push(lines, render("h4", "See also"))
for (i in docblock["see"]) {
item = render("li", render_toc_link(docblock["see"][i]))
item = render("li-preprocess", render_toc_link(docblock["see"][i]))
item = render("li", item)
push(lines, item)
}

Expand All @@ -551,6 +561,92 @@ function debug(msg) {
debug("line: [" $0 "]")
}

# Previous line added a new docblock item.
# Check if current line has the needed indentation
# for it to be a multiple lines docblock item.
#
# This process must be done before any @ tag detection.
multiple_line_tag {
# Determine if the tag allow for next line without additionnal indentation.
# This should be only be true for @description and @example tags, for the moment.
no_indentation_match = ""
if (multiple_line_tag ~ /^(description|example)$/) {
no_indentation_match = sprintf("|%s[^@].*", after_hash_indentation)
}
multiple_line_identation_regex = sprintf( \
"^%s([[:blank:]]*|%s([[:blank:]]+[^[:blank:]]).*%s)$", \
hash_indentation, \
after_hash_indentation, \
no_indentation_match \
)

# Check if current line indentation does match the previous line docblock item.
if (match($0, multiple_line_identation_regex, contents)) {
debug("→ → @" multiple_line_tag " next line")
additional_line = contents[1]

# Detect text internal indentation.
if(trim(additional_line) != "" \
&& match(additional_line, /^([[:blank:]]*)([^[:blank:]].*)?$/, detected_indentation))
{
# Detect the minimal indentation of the text.
if(minimal_indentation == -1 \
|| length(minimal_indentation) > length(detected_indentation[1])) {
minimal_indentation = detected_indentation[1]
}
}

# Remove trailing spaces.
sub(/[[:space:]]+$/, "")

# Push matched message to corresponding docblock.
# docblock_append(multiple_line_docblock_name, "\n" $0)
text = concat(text, additional_line)

# Stop processing current line, and process next line.
next
} else {
# End of the multiple line tag.
debug("→ → END of @" multiple_line_tag)

# Remove minimal indentation from text.
if(minimal_indentation != -1) {
debug("→ → removing indentation from @ " multiple_line_tag " (length: " length(minimal_indentation) ")")
split(text, text_lines, "\n")
text = ""
for (i = 0; i < length(text_lines); i++) {
current_line = text_lines[i]
sub("^" minimal_indentation,"", current_line)
text = concat(text, current_line)
}
}

# Remove empty lines at the start of text.
sub(/^[[:space:]\n]*\n/, "", text)
# Remove empty lines at the end of text.
sub(/[[:space:]\n]*$/, "", text)

## Print final text on debug output.
debug("→ → Final text for @" multiple_line_tag " : [\n" text "\n]")

if(multiple_line_tag == "description") {
# If current tag is a description.
# Call handle_description with description set as the multiline text.
handle_description(text)
} else if (multiple_line_tag ~ /^(stdin|stdout|stderr|set|exitcode|see)$/) {
# If current tag is a multiple occurence tag.
# Push multi-line text as new item of the corresponding docblock.
docblock_push(multiple_line_tag, text)
} else {
docblock_set(multiple_line_tag, text)
}

# End previous line docblock item.
multiple_line_tag = ""
}
}


/^[[:space:]]*# @internal/ {
debug("→ @internal")
is_internal = 1
Expand All @@ -574,34 +670,33 @@ function debug(msg) {
next
}

/^[[:space:]]*# @description/ {
debug("→ @description")
in_description = 1
in_example = 0

handle_description()

reset()
}

in_description {
if (/^[^[[:space:]]*#]|^[[:space:]]*# @[^d]|^[[:space:]]*[^#]|^[[:space:]]*$/) {
debug("→ → in_description: leave")
# Process @description entries.
# Allow for multiple lines entries.
match($0, /^([[:blank:]]*#)([[:blank:]]+)@(description|example|stdin|stdout|stderr|set|exitcode|see)[[:blank:]]*(.*[^[:blank:]])?[[:blank:]]*$/, contents) {
# Fetch matched values.
hash_indentation = contents[1]
after_hash_indentation = contents[2]
tag_name = contents[3]
if(tag_name == "example") {
# For @example tag, the content of the tag line is ignored.
text = ""
} else {
text = trim(contents[4])
}
# minimal indentation is used to detect global indentation of the multiple line text.
# Line where the tag (e.g. @description) is is considered to have no indentation.
minimal_indentation = -1

in_description = 0
debug("→ @" tag_name)

handle_description()
} else {
debug("→ → in_description: concat")
sub(/^[[:space:]]*# @description[[:space:]]*/, "")
sub(/^[[:space:]]*#[[:space:]]*/, "")
sub(/^[[:space:]]*#$/, "")
# Signal the start of a multiple line tag.
multiple_line_tag = tag_name

description = concat(description, $0)
next
}
# Stop processing current line, and process next line.
next
}


/^[[:space:]]*# @section/ {
debug("→ @section")
sub(/^[[:space:]]*# @section /, "")
Expand All @@ -610,29 +705,6 @@ in_description {
next
}

/^[[:space:]]*# @example/ {
debug("→ @example")

in_example = 1


next
}

in_example {
if (! /^[[:space:]]*#[ ]{1,}/) {
debug("→ → in_example: leave")
in_example = 0
} else {
debug("→ → in_example: concat")
sub(/^[[:space:]]*#/, "")

docblock_concat("example", $0)
next
}

}

# Select @option lines with content.
/^[[:blank:]]*#[[:blank:]]+@option[[:blank:]]+[^[:blank:]]/ {
debug("→ @option")
Expand Down Expand Up @@ -702,79 +774,6 @@ in_example {
next
}

/^[[:space:]]*# @set/ {
debug("→ @set")
sub(/^[[:space:]]*# @set /, "")

docblock_push("set", $0)

next
}

/^[[:space:]]*# @exitcode/ {
debug("→ @exitcode")
sub(/^[[:space:]]*# @exitcode /, "")

docblock_push("exitcode", $0)

next
}

/^[[:space:]]*# @see/ {
debug("→ @see")
sub(/[[:space:]]*# @see /, "")

docblock_push("see", $0)

next
}

# Previous line added a new docblock item.
# Check if current line has the needed indentation
# for it to be a multiple lines docblock item.
multiple_line_docblock_name {
# Check if current line indentation does match the previous line docblock item.
if ($0 ~ multiple_line_identation_regex ) {
debug("→ @" multiple_line_docblock_name " - new line")

# Current line has the same indentation as the stderr section.

# Remove indentation and trailing spaces.
sub(/^[[:space:]]*#[[:space:]]+/, "")
sub(/[[:space:]]+$/, "")

# Push matched message to corresponding docblock.
docblock_append(multiple_line_docblock_name, "\n" $0)

# Stop processing current line, and process next line.
next
} else {
# End previous line docblock item.
multiple_line_docblock_name = ""
}
}

# Process similarly @stdin, @stdout and @stderr entries.
# Allow for multiple lines entries.
match($0, /^([[:blank:]]*#[[:blank:]]+)@(stdin|stdout|stderr)[[:blank:]]+(.*[^[:blank:]])[[:blank:]]*$/, contents) {
# Fetch matched values.
indentation = contents[1]
docblock_name = contents[2]
text = contents[3]

debug("→ @" docblock_name)

# Push matched message to corresponding docblock.
docblock_push(docblock_name, text)

# Signal the start of a multiple line section.
multiple_line_docblock_name = docblock_name
multiple_line_identation_regex = "^" indentation "[[:blank:]]+[^[:blank:]].*$"

# Stop processing current line, and process next line.
next
}

/^[ \t]*(function([ \t])+)?([a-zA-Z0-9_\-:-\\.]+)([ \t]*)(\(([ \t]*)\))?[ \t]*\{/ \
&& (length(docblock) != 0 || description != "") && !in_example {
debug("→ function")
Expand All @@ -801,7 +800,7 @@ match($0, /^([[:blank:]]*#[[:blank:]]+)@(stdin|stdout|stderr)[[:blank:]]+(.*[^[:

/^[^#]*$/ {
debug("→ break")
handle_description();
#handle_description();
reset()
next
}
Expand Down
Loading

0 comments on commit 9cfcde9

Please sign in to comment.