Summary Files Commits Branches @commit @diff @stats
path: root / md-parser.awk
diff options
context:
space:
mode:
Commit828ef67391a1a4b4d6d0fc7e466d4678c5decc0d (patch)
Author
 Syndamia <kamen@syndamia.com>
2025-03-03 09:48:07 +0200
Committer
 Syndamia <kamen@syndamia.com>
2025-03-03 09:48:07 +0200
Files6b89eb1fa77b75c560a1b969daa2e3397a3c0e10 /md-parser.awk
csg-828ef67391a1a4b4d6d0fc7e466d4678c5decc0d.tar
csg-828ef67391a1a4b4d6d0fc7e466d4678c5decc0d.tar.gz
csg-828ef67391a1a4b4d6d0fc7e466d4678c5decc0d.zip
Parent7f2a2075423fa409e5cbc0903260a899bc47fb9a (diff)
feat(parser): Rework completion
1 files changed, 160 insertions, 164 deletions
-rw-r--r--md-parser.awk324
diff --git a/md-parser.awk b/md-parser.awk
index 7f3519b..2161482 100644
--- a/md-parser.awk
+++ b/md-parser.awk
@@ -61,16 +61,51 @@ BEGIN {
#== Block markup ==#
-# Inline HTML
-/^[ \t]?[ \t]?[ \t]?<\/?[0-9A-Za-z]+/ {
- # if it isn't inside a list
- if (indentation() - 2 * list_level < 4) {
- STATE_set(STATE_HTML)
- soft_next()
+#-- Blockquotes --#
+blockquote_level > 0 && empty0 {
+ STATE_unset(STATE_PARAGRAPH_P1)
+ if (STATE_thenunset(STATE_CODEBLOCK)) {
+ PNappnd("</code></pre>")
+ }
+ for (P1 = P1 ORS; blockquote_level > 0; --blockquote_level) {
+ P1 = P1 "</blockquote>"
+ }
+}
+/^>[ \t]/ {
+ STATE_set(STATE_BLOCKQUOTE)
+
+ match($0, /^[> \t]*> /)
+ blevel = substr($0, RSTART, RLENGTH)
+
+ $0 = substr($0, RSTART + RLENGTH)
+ gsub(/[ \t]/, "", blevel)
+ blevel = length(blevel)
+
+ for (;blockquote_level < blevel; ++blockquote_level) {
+ P1 = P1 "<blockquote>"
+ }
+ for (;blockquote_level > blevel; --blockquote_level) {
+ P2 = P2 "</blockquote>"
}
}
+STATE_isset(STATE_BLOCKQUOTE) && $0 == ">" {
+ $0 = ""
+ empty0 = TRUE
+}
+
+#-- Remove list indentation --#
+list_level > 0 && !empty0 && !is_blank() {
+ level = int((indentation() + 2) / 2)
+ sub("^[ \t]{1," lists_shift[list_level] "}", "")
+}
+
+#-- Inline HTML --#
+!STATE_isset(STATE_CODEBLOCK) && /^[ \t]{0,3}<\/?[0-9A-Za-z]+/ {
+ STATE_set(STATE_HTML)
+ soft_next()
+}
STATE_isset(STATE_HTML) {
- if (/^[ \t]*$/) {
+ if (is_blank()) {
STATE_unset(STATE_HTML)
}
else {
@@ -78,101 +113,88 @@ STATE_isset(STATE_HTML) {
}
}
-# Fenced code blocks
+#-- Fenced code blocks --#
STATE_isset(STATE_FENCED_CODEBLOCK) {
- if (fenced_class) {
- $0 = fenced_class $0
- fenced_class = ""
+ if (fenced_start) {
+ $0 = fenced_start $0
+ fenced_start = ""
soft_next()
}
- $0 = " " $0
+ if (/^```/) {
+ PNappnd("</code></pre>")
+ STATE_unset(STATE_CODEBLOCK)
+ STATE_unset(STATE_FENCED_CODEBLOCK)
+ next
+ }
}
-# Code blocks (ending condition)
+#-- Code blocks --#
STATE_isset(STATE_CODEBLOCK) {
- # If ended codeblock
- if ((STATE_isset(STATE_FENCED_CODEBLOCK) && / ```$/) ||
- ! /^( |\t|[ \t]*$)/ ||
- (blockquote_level > 0 && empty0))
- {
+ if (!empty0 && !STATE_isset(STATE_FENCED_CODEBLOCK) && ! /^( |\t)/) {
PNappnd("</code></pre>")
STATE_unset(STATE_CODEBLOCK)
- emptyP1 = 1
- if (STATE_thenunset(STATE_FENCED_CODEBLOCK)) {
- next
- }
}
- else if (!STATE_isset(STATE_BLOCKQUOTE)) {
- handle_codeblock()
+ else {
+ escape_in_codeblock()
soft_next()
}
}
-# Blockquotes
-blockquote_level > 0 && empty0 {
- STATE_unset(STATE_PARAGRAPH_P1)
- for (P1 = P1 ORS; blockquote_level > 0; --blockquote_level) {
- P1 = P1 "</blockquote>"
- }
-}
-/^>[ \t]/ {
- match($0, /^[> \t]*> /)
- level = substr($0, RSTART, RLENGTH)
-
- $0 = substr($0, RSTART + RLENGTH)
- gsub(/[ \t]/, "", level)
- level = length(level)
-
- STATE_set(STATE_BLOCKQUOTE)
- for (;blockquote_level < level; ++blockquote_level) {
- P1 = P1 "<blockquote>"
- }
- for (;blockquote_level > level; --blockquote_level) {
- P2 = P2 "</blockquote>"
- }
-}
-STATE_isset(STATE_BLOCKQUOTE) && $0 == ">" { $0 = "" }
-
-# Code block (end)
-STATE_isset(STATE_CODEBLOCK) { ## when in blockquote
- handle_codeblock()
- soft_next()
-}
-
-# Link definition
-/^\[[^\]]+\]:[ \t]+.*/ { # line starts link definition
+#-- Link definition (ignore) --#
+/^\[[^\]]+\]:[ \t]+.*/ {
next
}
-# Escapes
+#-- Escapes --#
/\\[-\\`*_{}\[\]()#+.!^~]/ { # has escaped char
while (match($0, /\\[-\\`*_{}\[\]()#+.!^~]/)) {
sub_match(escapes[char($0, RSTART + 1)])
}
}
-# Horizontal rule
-/^(---+|- - -( -)*|\*\*\*+|\* \* \*( \*)*|___+|_ _ _( _)*)$/ {
+#-- Horizontal rule --#
+/^(---+|- -( -)+|\*\*\*+|\* \*( \*)+|___+|_ _( _)+)$/ {
STATE_unset(STATE_PARAGRAPH)
$0 = "<hr/>"
}
-# Lists
-function end_list() {
- STATE_unset(STATE_LIST)
+#-- Lists --#
+/^[ \t]*[*+-] / { ## Unordered list
+ level = level + int(indentation() / 2)
- if (list_level == 0) return
- PNappnd("</li></" (lists[list_level] == "." ? "o" : "u") "l>")
- --list_level
+ match($0, /^[ \t]*[*+-] /)
+ type = char($0, RSTART + RLENGTH - 2)
+ shift = RLENGTH
+ sub_match("")
+
+ start_list(level, type, shift)
+ level = 0
}
+/^[ \t]*[0-9]+\. / { ## Ordered list
+ level = level + int(indentation() / 2)
+
+ type = "."
+ match($0, /^[^.]+\. /)
+ shift = RLENGTH
+ sub_match("")
+
+ start_list(level, type, shift)
+ level = 0
+}
+level > 0 && list_level > 0 && !is_blank() && emptyP1 { ## Non-list line
+ while (list_level >= level) {
+ end_list()
+ }
+}
+list_level > 0 {
+ STATE_unset(STATE_PARAGRAPH)
+}
+
function start_list(level, type, shift) {
STATE_set(STATE_LIST)
- brand_new = TRUE
## Every two columns we define a new level
level = int((level + 2) / 2)
- ## If list definition is the first line inside a code block
- if (level - list_level >= 3) return
## Make list_level = level - 1
while (list_level < level - 1) {
@@ -197,85 +219,55 @@ function start_list(level, type, shift) {
P1 = P1 ORS "<li>"
}
}
-/^[ \t]*[*+-] / { ## Unordered list
- level = indentation()
-
- match($0, /^[^*+-]*[*+-] /)
- type = char($0, RSTART + RLENGTH - 2)
- shift = RLENGTH
- sub_match("")
-
- start_list(level, type, shift)
-}
-/^[ \t]*[0-9]+\. / { ## Ordered list
- level = indentation()
-
- type = "."
- match($0, /^[^.]+\. /)
- shift = RLENGTH
- sub_match("")
-
- start_list(level, type, shift)
-}
-!brand_new && list_level > 0 && /[^ \t]/ { ## Non-list line
- level = int((indentation() + 2) / 2)
- sub("^[ \t]{1," lists_shift[list_level] "}", "")
+function end_list() {
+ STATE_unset(STATE_LIST)
- while (list_level >= level && emptyP1) {
- end_list()
- }
-}
-list_level > 0 {
- STATE_unset(STATE_PARAGRAPH)
- brand_new = FALSE
+ if (list_level == 0) return
+ PNappnd("</li></" (lists[list_level] == "." ? "o" : "u") "l>")
+ --list_level
}
-# Checklists
-STATE_isset(STATE_LIST) && /^\[[x ]\][ \t]/ {
+#-- Checklists --#
+STATE_isset(STATE_LIST) && level == 0 && /^\[[x ]\][ \t]/ {
checked = (char($0, 2) == "x") ? " checked=\"checked\"" : "";
$0 = "<input type=\"checkbox\" disabled=\"disabled\"" checked \
"><label>" substr($0, 5) "</label>"
}
-# Fenced code blocks
+#-- Fenced code blocks (new) --#
/^[ \t]*```/ {
STATE_set(STATE_FENCED_CODEBLOCK)
STATE_set(STATE_CODEBLOCK)
match(P1, /```[^ \t]*/)
if (RLENGTH > 3) {
- fenced_class = " class=\"language-" tolower(substr(P1, RSTART + 3, RLENGTH - 3)) "\""
+ fenced_start = " class=\"language-" tolower(substr(P1, RSTART + 3, RLENGTH - 3)) "\""
}
else {
- fenced_class = ""
+ fenced_start = ""
}
- fenced_class = "<pre><code" fenced_class ">"
+ fenced_start = "<pre><code" fenced_start ">"
next
}
-# Code blocks
-function handle_codeblock() {
- ## Autoescape & and <
+#-- Code blocks (new) --#
+/^( |\t)/ && /[^ \t]/ {
+ STATE_set(STATE_CODEBLOCK)
+ escape_in_codeblock()
+ $0 = "<pre><code>" $0
+ soft_next()
+}
+function escape_in_codeblock() {
for (i = 1; match(substr($0, i), /&/); i += RSTART) {
RSTART += i - 1
sub_match("&amp;")
}
gsub(/</, "\\&lt;")
gsub(/>/, "\\&gt;")
-
- if (STATE_isset(STATE_FENCED_CODEBLOCK)) {
- $0 = substr($0, 5)
- }
-}
-/^( |\t)/ && /[^ \t]/ { ## New codeblock
- STATE_set(STATE_CODEBLOCK)
- handle_codeblock()
- $0 = "<pre><code>" $0
- soft_next()
}
-# Definition lists
+#-- Definition lists --#
STATE_isset(STATE_DEFINITION_LIST) {
if (!empty0) {
STATE_unset(STATE_PARAGRAPH)
@@ -319,7 +311,20 @@ STATE_isset(STATE_DEFINITION_LIST) {
#== Inline markup ==#
-# Automatic links
+#-- Autoescape & --#
+/&/ {
+ for (i = 1; match(substr($0, i), /&/); i += RSTART) {
+ RSTART += i - 1
+ if (substr($0, RSTART) ~ /&[A-Za-z0-9#]+;/) { continue }
+ sub_match("&amp;")
+ }
+}
+#-- Autoescape < --#
+/ < / {
+ gsub(/ < /, "\\&lt;")
+}
+
+#-- Automatic links --#
/<[^ \t<>]+>/ {
## URL: <uri_scheme:...>
while (match($0, /<[A-Za-z][-A-Za-z0-9+.]*:[^ \t<>]+>/)) {
@@ -343,25 +348,12 @@ STATE_isset(STATE_DEFINITION_LIST) {
}
}
-# Autoescape &
-/&/ {
- for (i = 1; match(substr($0, i), /&/); i += RSTART) {
- RSTART += i - 1
- if (substr($0, RSTART) ~ /&[A-Za-z0-9#]+;/) { continue }
- sub_match("&amp;")
- }
-}
-# Autoescape <
-/ < / {
- gsub(/ < /, "\\&lt;")
-}
-
-# Line break
+#-- Line break --#
/ $/ {
sub(/[ \t]* $/, "</br>")
}
-# atx-style headers
+#-- atx-style headers --#
/^[ \t]*#+/ {
match($0, /#+/)
if (RLENGTH <= 6) {
@@ -374,7 +366,7 @@ STATE_isset(STATE_DEFINITION_LIST) {
}
}
-# setex-style headers
+#-- setex-style headers --#
!emptyP1 && /^[ \t]*(=|-)+[ \t]*$/ {
hlevel = 1 + ($0 ~ /-/)
P1 = "<h" hlevel ">" substr(P1, 4) "</h" hlevel ">"
@@ -382,7 +374,25 @@ STATE_isset(STATE_DEFINITION_LIST) {
STATE_unset(STATE_PARAGRAPH)
}
-# Emphasis
+#-- Emphasis --#
+{
+ ## Make sure <em> is on the inside for long chains
+ ## E.g. *****a***** will be the same as ****_a_****
+ surround("___" , "<strong><em>", "</em></strong>")
+ surround("\\*\\*\\*", "<strong><em>", "</em></strong>", 3)
+
+ gsurround("__" , "<strong>", "</strong>")
+ gsurround("_" , "<em>" , "</em>")
+ gsurround("\\*\\*", "<strong>", "</strong>", 2)
+ gsurround("\\*" , "<em>" , "</em>", 1)
+ gsurround("``" , "<code>" , "</code>")
+ gsurround("`" , "<code>" , "</code>")
+
+ gsurround("~~" , "<s>" , "</s>")
+ gsurround("~" , "<sub>" , "</sub>")
+ gsurround("\\^", "<sup>" , "</sup>", 1)
+}
+
function surround(brace, left, right, blen) {
if (blen == "") { blen = length(brace) }
if (match($0, brace (brace == "``" ? "." : "[^" brace "]") "+" brace)) {
@@ -405,31 +415,13 @@ function gsurround(brace, left, right, blen) {
}
}
-{
- ## Make sure <em> is on the inside for long chains
- ## E.g. *****a***** will be the same as ****_a_****
- surround("___" , "<strong><em>", "</em></strong>")
- surround("\\*\\*\\*", "<strong><em>", "</em></strong>", 3)
-
- gsurround("__" , "<strong>", "</strong>")
- gsurround("_" , "<em>" , "</em>")
- gsurround("\\*\\*", "<strong>", "</strong>", 2)
- gsurround("\\*" , "<em>" , "</em>", 1)
- gsurround("``" , "<code>" , "</code>")
- gsurround("`" , "<code>" , "</code>")
-
- gsurround("~~" , "<s>" , "</s>")
- gsurround("~" , "<sub>" , "</sub>")
- gsurround("\\^", "<sup>" , "</sup>", 1)
-}
-
-# Footnote references
+#-- Footnote references --#
/\[\^[^\]]+\]/ {
while (match($0, /\[\^[\]]+\]/)) {
}
}
-# Links
+#-- Links --#
/!?\[[^\]]*\] ?[(\[][^)\]]*[\])]/ {
while (match($0, /!?\[[^\]]*\] ?[(\[][^)\]]*[\])]/)) {
link = substr($0, RSTART, RLENGTH)
@@ -468,11 +460,11 @@ function gsurround(brace, left, right, blen) {
}
}
-# Paragraph
+#-- Paragraph --#
emptyP1 && !empty0 && STATE_isset(STATE_PARAGRAPH) { $0 = "<p>" $0 }
!emptyP1 && empty0 && STATE_isset(STATE_PARAGRAPH_P1) { P1 = P1 "</p>" }
-# After all $0 parsing
+#== Buffered printing ==#
{
__buffered_print()
}
@@ -538,6 +530,7 @@ END {
}
#== Boolean "constants" ==#
+
BEGIN {
FALSE = 0
TRUE = 1
@@ -599,17 +592,25 @@ function indentation( len) {
return len
}
+function is_blank() {
+ return ! /[^ \t]/
+}
+
#== Match handling ==#
function save_match() {
backup_rstart = RSTART
backup_rlength = RLENGTH
}
+
function restore_match() {
RSTART = backup_rstart
RLENGTH = backup_rlength
}
+function sub_match(replacement) {
+ $0 = substr($0, 1, RSTART - 1) replacement substr($0, RSTART + RLENGTH)
+}
#== Other ==#
@@ -629,14 +630,9 @@ function soft_next() {
next
}
-function sub_match(replacement) {
- $0 = substr($0, 1, RSTART - 1) replacement substr($0, RSTART + RLENGTH)
-}
-
function char(string, ind) {
if (1 > ind || ind > length(string)) {
return "";
}
return substr(string, ind, 1)
}
-