// Custom whitespace that does NOT include newlines
OMITTED_WHITESPACE = _{ (" " | "\t")+ }
WHITESPACE = { OMITTED_WHITESPACE }
OMITTED_NEWLINE = _{ "\n" | "\r\n" | "\r" }
NEWLINE = { OMITTED_NEWLINE }

// The document is a sequence of blocks separated by blank lines
document = ${
    SOI ~
    frontmatter?
    ~
    (NEWLINE | WHITESPACE)*
    ~
    (
        newline_separated_blocks_without_the_delimiting_newlines
        ~ (
            NEWLINE+ ~ (OMITTED_WHITESPACE ~ NEWLINE+)*
                ~ newline_separated_blocks_without_the_delimiting_newlines
        )*
    )
    ~ (NEWLINE+ ~ (OMITTED_WHITESPACE ~ NEWLINE+)*)?
    ~ EOI
}

frontmatter = {
    "===" ~ (OMITTED_NEWLINE+ ~ frontmatter_line)* ~ OMITTED_NEWLINE+ ~ "..." ~ OMITTED_NEWLINE
}

frontmatter_line = {
    (!("..."? ~ OMITTED_NEWLINE) ~ ANY) ~ (!OMITTED_NEWLINE ~ ANY)*
}

newline_separated_blocks_without_the_delimiting_newlines = {
    (non_line_block ~ NEWLINE+)* ~ (non_line_block | line) ~ &(NEWLINE | EOI)
}

non_line_block = {
    (heading
    | hr
    | code_fence
    | blockquote
    | unordered_list
    | ordered_list)
}

heading = {
    h1 | h2 | h3 | h4 | h5 | h6
}

h1 = { ("#1" | "#") ~ (OMITTED_WHITESPACE)* ~ !(OMITTED_WHITESPACE) ~ h1_inner }
h2 = { ("#2" | "##") ~ (OMITTED_WHITESPACE)* ~ !(OMITTED_WHITESPACE) ~ h2_inner }
h3 = { ("#3" | "###") ~ (OMITTED_WHITESPACE)* ~ !(OMITTED_WHITESPACE) ~ h3_inner }
h4 = { ("#4" | "####") ~ (OMITTED_WHITESPACE)* ~ !(OMITTED_WHITESPACE) ~ h4_inner }
h5 = { ("#5" | "#####") ~ (OMITTED_WHITESPACE)* ~ !(OMITTED_WHITESPACE) ~ h5_inner }
h6 = { ("#6" | "######") ~ (OMITTED_WHITESPACE)* ~ !(OMITTED_WHITESPACE) ~ h6_inner }

h1_inner = {
    (!NEWLINE ~ ANY)+
}

h2_inner = {
    (!NEWLINE ~ ANY)+
}

h3_inner = {
    (!NEWLINE ~ ANY)+
}

h4_inner = {
    (!NEWLINE ~ ANY)+
}

h5_inner = {
    (!NEWLINE ~ ANY)+
}

h6_inner = {
    (!NEWLINE ~ ANY)+
}

hr = { "--" ~ "-"+ }

code_fence = {
    fence_open ~
    (code_line? ~ (NEWLINE ~ code_line)*)?
    ~ fence_close
}

fence_open = {
    "```" ~ (code_info)? ~ NEWLINE
}

code_info = {
    !("\n") ~ (!NEWLINE ~ ANY)*
}

code_line = {
    !("```") ~ (!NEWLINE ~ ANY)*
}

fence_close = {
    "```"
}

blockquote = {
    (blockquote_line ~ (NEWLINE ~ blockquote_line)*)
}

blockquote_line = {
    blockquote_marker ~ (OMITTED_WHITESPACE? ~ line)
}

blockquote_marker = {
    ">" ~ OMITTED_WHITESPACE?
}

unordered_list = {
    (unordered_list_item ~ (NEWLINE ~ unordered_list_item)*)
}

unordered_list_item = {
    unordered_list_marker ~ (OMITTED_WHITESPACE? ~ line)
}

unordered_list_marker = {
    "*" | "-" | "+"
}

ordered_list = {
    (ordered_list_item ~ (NEWLINE ~ ordered_list_item)*)
}

ordered_list_item = {
    ordered_list_marker ~ (OMITTED_WHITESPACE? ~ line)
}

ordered_list_marker = {
    ASCII_DIGIT+ ~ "."
}

line = {
    words ~ WHITESPACE? ~ &(NEWLINE | EOI)
}

words = {
    inline ~ (WHITESPACE ~ inline)*
}

inline = {
    inline_left_word_boundary ~ inline_no_word_boundary*
}

inline_left_word_boundary = {
    bold_on
    | italic_on
    | underline_on
    | strikethrough_on
    | spoiler_on
    | code_span
    | superscript
    | subscript
    | link
    | image
    | color
    | plain_text
}

inline_no_word_boundary = {
    bold_off
    | italic_off
    | underline_off
    | strikethrough_off
    | spoiler_off
    | code_span
    | superscript
    | subscript
    | link
    | image
    | color
    | plain_text
}

bold_on = {
    "**" ~ !"*"
}

italic_on = {
    "/" ~ !"/"
}

underline_on = {
    "_" ~ !"_"
}

strikethrough_on = {
    "~" ~ !"~"
}

spoiler_on = {
    "||" ~ !"|"
}

bold_off = {
    "**" ~ !"*"
}

italic_off = {
    "/" ~ !"/"
}

underline_off = {
    "_" ~ !"_"
}

strikethrough_off = {
    "~" ~ !"~"
}

spoiler_off = {
    "||" ~ !"|"
}

code_span = {
    "`" ~ code_span_inner ~ "`"
}

code_span_inner = {
    (!"`" ~ ANY)+
}

superscript = {
    "^(" ~ superscript_inner ~ ")^"
}

superscript_inner = {
    (!")^" ~ ANY)+
}

subscript = {
    "^v(" ~ subscript_inner ~ ")v"
}

subscript_inner = {
    (!")v" ~ ANY)+
}

// Links: ((text|url))
link = {
    "((" ~ link_text ~ "|" ~ link_url ~ "))"
}

link_text = {
    (!("|" | "))") ~ ANY)+
}

link_url = {
    (!"))" ~ ANY)+
}

// Images: !((alt|url))
image = {
    "!((" ~ (image_alt ~ "|")? ~ image_url ~ "))"
}

image_alt = {
    (!("|" | "))") ~ ANY)+
}

image_url = {
    (!"))" ~ ANY)+
}

// Color: %(#c0ffee) ... %()
color = {
    "%(" ~ color_code ~ ")"
}

color_code = {
    (!")" ~ ANY)*
}

// Font size: %=123.0= ... %==
font_size = {
    "%=" ~ font_size_number ~ "="
}

font_size_number = {
    (ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT*)?)?
}

// Variable substitution: {{foo}}

variable_substitution = {
    "{{" ~ variable_substitution_identifier ~ "}}"
}

variable_substitution_identifier = {
    (!"}}" ~ ANY)+
}

// Plain text - matches any characters not consumed by other inline rules
plain_text = {
    !(NEWLINE | WHITESPACE) ~ ANY
}
