diff options
| Commit | 6e6789af2897b6a73766db33eb174e1ac7bb3f68 (patch) | |
|---|---|---|
| Author | 2024-12-31 15:04:35 +0200 | |
| Committer | 2024-12-31 15:04:35 +0200 | |
| Files | 56c39828eb8c58b0d09df8fa49546bb900463684 /single.awk | |
| csg-6e6789af2897b6a73766db33eb174e1ac7bb3f68.tar csg-6e6789af2897b6a73766db33eb174e1ac7bb3f68.tar.gz csg-6e6789af2897b6a73766db33eb174e1ac7bb3f68.zip | ||
| Parent | 5a53c0bfc65d8966ee0874939dfe449e372330f7 (diff) | |
feat(single)!: Rewrite using buffered output
This should provide a significantly simpler codebase.
Implemented paragraphs, line break, headers and blockquotes.
1 files changed, 99 insertions, 298 deletions
| -rw-r--r-- | single.awk | 397 |
@@ -1,34 +1,34 @@ # Status: # [.] Inline HTML -# [x] Autoescape +# [ ] Autoescape # [.] Block elements # [x] Paragraph # [x] Line break # [x] Headers (atx and setex) # [x] Blockquotes -# [x] Lists +# [ ] Lists # [ ] Code blocks -# [x] Horizontal rules -# [x] Span elements -# [x] Links -# [x] Inline -# [x] Reference -# [x] Emphasis -# [x] Code -# [x] Images -# [x] Inline -# [x] Reference +# [ ] Horizontal rules +# [ ] Span elements +# [ ] Links +# [ ] Inline +# [ ] Reference +# [ ] Emphasis +# [ ] Code +# [ ] Images +# [ ] Inline +# [ ] Reference # [ ] Other # [ ] Autolinks # [ ] Escapes # -# [.] Non-standard features -# [x] Strikethrough ~~...~~ -# [x] Superscript ^...^ -# [x] Subscript ~...~ +# [ ] Non-standard features +# [ ] Strikethrough ~~...~~ +# [ ] Superscript ^...^ +# [ ] Subscript ~...~ # [ ] Footnote [^...] (with user customizations) -# [x] Task list - [ ] (readonly) -# [.] Definition lists +# [ ] Task list - [ ] (readonly) +# [ ] Definition lists # [x] Single line # [ ] Multi-line # [ ] Fenced code blocks ```lang ``` @@ -46,94 +46,56 @@ # - emphasis inside inline code block is still converted to HTML # - backticks inside `` emphasis blocks aren't escaped -BEGIN { - prev_blankline = blankline = 1 - skipprint = 1 - newStack("bq-level") - newStack("list-level") -} - -function println(line) { - if (0 == line) line = $0 - print prev - prev = line -} - -function autoinsert(arrname, isbefore) { - if (isbefore == "") isbefore = 0 - for (confstr in arrname) { - split(confstr, conf, SUBSEP) - - ## FILENAME regex, ".*" (default) - if (2 in conf && FILENAME !~ conf[2]) { continue } - ## Text regex, ".*" (default) - if (3 in conf && text !~ conf[3]) { continue } - - if (isbefore) prev = arrname[confstr] prev - else prev = prev arrname[confstr] - - ## When, FIRST | ALL - if (conf[1] == FIRST) { delete arrname[confstr] } - } +function empty(value) { + return value == "" } { - prev_blankline = blankline - blankline = 1 + not_paragraph_p1 = not_paragraph + not_paragraph = 0 + empty0 = empty($0) + emptyP1 = empty(P1) } -/[^ \t]/ { - blankline = 0 -} +# Output is buffered on multiple lines, held in these variables: +# P2 = previous non-empty line +# P1 = previous line +# $0 = current line +# N1 = next new line (N1 is always set by the user) # Inline HTML /^[ \t]*<\/?[0-9A-Za-z]+/ { - println() - next + not_paragraph = 1 } -# Link definition -/^\[.+\]:/ { - blankline = 1 - next +# Blockquotes +blockquote_level > 0 && empty0 { + for (P1 = P1 ORS; blockquote_level > 0; --blockquote_level) { + P1 = P1 "</blockquote>" + } + not_paragraph_p1 = 1 } +/^>[ \t]/ { + match($0, /^[> \t]+[ \t]+/) + level = substr($0, RSTART, RLENGTH) + $0 = substr($0, RSTART + RLENGTH) + gsub(/[ \t]/, "", level) + level = length(level) -# Auto-escape & -/&/ { - ampstart = 0 - ## gsub(/&(?![[:alnum:]#]+;)/, "&") - while (match(substr($0, ampstart + 1), /&/)) { - ampstart += RSTART - if (match(substr($0, ampstart), /&[0-9A-Za-z#]+;/)) { continue } - - $0 = substr($0, 1, ampstart - 1) "&" substr($0, ampstart + 1) + for (;blockquote_level < level; ++blockquote_level) { + P1 = P1 "<blockquote>" } + if (blockquote_level > level) { P2 = P2 ORS } + for (;blockquote_level > level; --blockquote_level) { + P2 = P2 "</blockquote>" + } + not_paragraph = 1 } -# Auto-escape < -/</ { - gsub(/</, "\\<") -} - -# Blockquote Part 1/2 -/^>[ \t]*$/ { next } -/^> / { - level = 0 - while (match($0, /^> [ \t]*/)) { - $0 = substr($0, RLENGTH + 1) - level += RSTART - while (topStack("bq-level") > level) { - println("</blockquote>") - autoinsert(user_BLOCKQUOTE_AFTER) +/^>$/ { $0 = ""; not_paragraph = 1 } - popStack("bq-level") - } - if (topStack("bq-level") < level) { - autoinsert(user_BLOCKQUOTE_BEFORE, 1) - println("<blockquote>") - - pushStack("bq-level", level) - } - } +# Line break +/ $/ { + sub(/[ \t]* $/, "</br>") } # atx-style headers @@ -144,232 +106,71 @@ function autoinsert(arrname, isbefore) { gsub(/^[ \t]*#+[ \t]*/, "") gsub(/[ \t]*#+[ \t]*$/, "") - if (RLENGTH == 1) autoinsert(user_H1_BEFORE, 1) - else if (RLENGTH == 2) autoinsert(user_H2_BEFORE, 1) - else if (RLENGTH == 3) autoinsert(user_H3_BEFORE, 1) - else if (RLENGTH == 4) autoinsert(user_H4_BEFORE, 1) - else if (RLENGTH == 5) autoinsert(user_H5_BEFORE, 1) - else if (RLENGTH == 6) autoinsert(user_H6_BEFORE, 1) - autoinsert(user_H_BEFORE, 1) - - println("<h" RLENGTH ">" $0 "</h" RLENGTH ">") - - autoinsert(user_H_AFTER) - if (RLENGTH == 1) autoinsert(user_H1_AFTER) - else if (RLENGTH == 2) autoinsert(user_H2_AFTER) - else if (RLENGTH == 3) autoinsert(user_H3_AFTER) - else if (RLENGTH == 4) autoinsert(user_H4_AFTER) - else if (RLENGTH == 5) autoinsert(user_H5_AFTER) - else if (RLENGTH == 6) autoinsert(user_H6_AFTER) - - blankline = 1 - next + $0 = "<h" RLENGTH ">" $0 "</h" RLENGTH ">" + not_paragraph = 1 } } + # setex-style headers -!prev_blankline && /^[ \t]*(=|-)+[ \t]*$/ { - sub(/^<p>/, "", prev) ## from paragraph logic +!emptyP1 && /^[ \t]*(=|-)+[ \t]*$/ { hlevel = 1 + ($0 ~ /-/) - - prev = "<h" hlevel ">" prev "</h" hlevel ">" - - if (hlevel == 1) autoinsert(user_H1_BEFORE, 1) - else autoinsert(user_H2_BEFORE, 1) - autoinsert(user_H_BEFORE, 1) - - autoinsert(user_H_AFTER) - if (hlevel == 1) autoinsert(user_H1_AFTER) - else autoinsert(user_H2_AFTER) - - blankline = 1 - next + P1 = "<h" hlevel ">" substr(P1, 4) "</h" hlevel ">" + $0 = "" + not_paragraph = 1 } -# Horizontal rules -/^(-|\*|_) ?(-|\*|_) ?(-|\*|_|[ \t])+$/ { - autoinsert(user_HR_BEFORE, 1) - println("<hr>") - autoinsert(user_HR_AFTER) - blankline = 1 - next -} +# Paragraph +emptyP1 && !empty0 && !not_paragraph { $0 = "<p>" $0 } +!emptyP1 && empty0 && !not_paragraph_p1 { P1 = P1 "</p>" } -# Line break -/ $/ { - sub(/[ \t]* $/, "</br>") -} - - -# Code blocks -/^( |\t)[ \t]*/ { -} +# After all $0 parsing +{ + empty0 = empty($0) + emptyP1 = empty(P1) -# Check boxes Part 1/2 -/^[ \t]*- \[[ x]\][ \t]/ { - if (checkbox_level <= 0) { - println("<ul class=\"checkboxed\">") - checkbox_level = 1 + if (empty0 && emptyP1) { + ++P1_repeats } - match($0, /- \[[ x]\][ \t]/) - if (substr($0, RSTART + 3, 1) == "x") checked = " checked=\"checked\"" - else checked = "" - $0 = "<li><input type=\"checkbox\" disabled=\"disabled\"" checked "><label>" substr($0, RSTART + RLENGTH) "</label></li>" -} - -# Unordered lists -function abs(x) { return ((x < 0) ? -x : x) } -!blankline && lengthStack("list-level") > 0 { - match($0, /^ */) - indent = RLENGTH - match($0, /(\*|\+|-|[0-9]+\.) /) - if (RLENGTH == -1) RLENGTH = 0 - ## indent + RLENGTH = length(" * " or " 1. ") - while (indent + RLENGTH < abs(topStack("list-level"))) { - if (topStack("list-level") < 0) { - println("</ol>") - autoinsert(user_L_AFTER) - autoinsert(user_OL_AFTER) - } - else { - println("</ul>") - autoinsert(user_L_AFTER) - autoinsert(user_UL_AFTER) - } - popStack("list-level") + else if (!empty0 && emptyP1) { + P1 = $0 } -} -/^[ \t]*(\*|\+|-|[0-9]+\.) / { - match($0, /(\*|\+|-|[0-9]+\.) /) - if (abs(topStack("list-level")) < RSTART) { - if (RLENGTH > 2) { - xl = "<ol>"; mult = -1 - autoinsert(user_OL_BEFORE, 1) + else { + print P2 + while (P1_repeats > 0) { + print "" + --P1_repeats } - else { - xl = "<ul>"; mult = 1 - autoinsert(user_UL_BEFORE, 1) + P2 = P1 + P1 = $0 + if (empty0) { + P1_repeats = 1 } - autoinsert(user_L_BEFORE, 1) - println(xl "<li>") - pushStack("list-level", mult * (RSTART + RLENGTH - 1)) - } - else { - println("</li><li>") } -} -!blankline && lengthStack("list-level") > 0 { - $0 = substr($0, abs(topStack("list-level")) + 1) -} - -# Paragraphs -prev_blankline && !blankline && lengthStack("bq-level") == 0 && lengthStack("list-level") == 0 && checkbox_level == 0 { $0 = "<p>" $0 } -!prev_blankline && blankline && lengthStack("bq-level") == 0 && lengthStack("list-level") == 0 && checkbox_level == 0 { prev = prev "</p>" } - -# Blockquote Part 2/2 -blankline { - while (lengthStack("bq-level") > 0) { - println("</blockquote>") - autoinsert(user_BLOCKQUOTE_AFTER) - popStack("bq-level") - } -} - -# Check boxes Part 2/2 -checkbox_level > 0 && ! /^<li><input type="checkbox" disabled="disabled"/ { - println("</ul>") - checkbox_level = 0 -} - -# Links -/!?\[[^\]]*\] ?[(\[][^)\]]*[\])]/ { - isimg = 0 - while (match($0, /!?\[[^\]]*\] ?[(\[]/)) { - if (substr($0, RSTART, 1) == "!") { - isimg = 1 - RSTART++ - RLENGTH-- - } - astart = RSTART ## astart = indexof("[") - has_space = substr($0, RSTART + RLENGTH - 2, 1) == " " - inner_anchor = substr($0, RSTART + 1, RLENGTH - 3 - has_space) ## ...[xxxxx](...)... - - aend = RSTART + RLENGTH - 1 - match(substr($0, astart), /\] ?[(\[][^)\]]*[\])]/) - aend += RLENGTH - 2 - has_space ## aend = indexof(")") - href = substr($0, astart + RSTART + 1 + has_space, RLENGTH - 3 - has_space) ## ...[...](xxxxx)... - title = "\"\"" - if (substr($0, astart + RSTART + has_space, 1) == "[") { ## is reference link - if (href == "") href = inner_anchor - title = reference[href, "title"] - href = reference[href, "href"] + if (!empty(N1)) { + if (emptyP1) { + P1 = N1 } - else if (match(href, /[ \t]"[^"]*"[ \t]*$/)) { ## has title - title = substr(href, RSTART + 1, RLENGTH - 1) - href = substr(href, 1, RSTART - 1) + else { + print P2 + while (P1_repeats > 0) { + print "" + --P1_repeats + } + P2 = P1 + P1 = N1 + N1 = "" } - sub(/[ \t]+/, "", href) - - if (isimg) elem = "<img src=\"" href "\" alt=\"" inner_anchor "\" title=" title ">" - else elem = "<a href=\"" href "\" title=" title ">" inner_anchor "</a>" - - $0 = substr($0, 1, astart - 1 - isimg) elem substr($0, aend + 1) - isimg = 0 } } -# Emphasis -function surround(brace, opening, closing, blen, single_surround) { - if (blen == "") { blen = length(brace) } - - while (match($0, brace "[^" brace "]+" brace)) { - elem = opening substr($0, RSTART + blen, RLENGTH - 2 * blen) closing - $0 = substr($0, 1, RSTART - 1) elem substr($0, RSTART + RLENGTH) - - if (single_surround != "") { return; } - } -} -{ - ## Make sure <em> is on the inside for long chains - ## E.g. *****a***** will be the same as ****_a_**** - surround("___" , "<strong><em>", "</em></strong>", 3, "oneshot") - surround("\\*\\*\\*", "<strong><em>", "</em></strong>", 3, "oneshot") - - surround("__" , "<strong>", "</strong>") - surround("_" , "<em>" , "</em>") - surround("\\*\\*", "<strong>", "</strong>", 2) - surround("\\*" , "<em>" , "</em>", 1) - surround("~~" , "<s>" , "</s>") - surround("~" , "<sub>" , "</sub>") - surround("\\^" , "<sup>" , "</sup>", 1) - surround("``" , "<code>" , "</code>") - surround("`" , "<code>" , "</code>") -} - -# Definition lists -!prev_blankline && /^: / { - sub(/^<p>/, "", prev) - sub(/^: /, "") - prev = "<dl><dt>" ORS prev ORS "</dt><dd>" ORS $0 ORS "</dd></dl>" - blankline = 1 - next -} - -{ - println() -} - END { - print prev - if (!blankline && lengthStack("bq-level") == 0) { print "</p>" } - ## Flush stacks - while (lengthStack("bq-level") > 0) { - print "</blockquote>" - popStack("bq-level") - } - while (lengthStack("list-level") > 0) { - if (topStack("list-level") < 0) print "</ol>" - else print "</ul>" - popStack("list-level") + print P2 + while (P1_repeats > 0) { + print "" + --P1_repeats } + if (!empty(P1)) { print P1 } + print + if (!empty(N1)) { print N1 } } |
