local babycode = {} ---renders babycode to html ---@param s string input babycode ---@param escape_html fun(s: string): string function that escapes html function babycode.to_html(s, escape_html) if not s or s == "" then return "" end -- extract code blocks first and store them as placeholders -- don't want to process bbcode embedded into a code block local code_blocks = {} local inline_codes = {} s = escape_html(s) local text = s:gsub("%[code%](.-)%[/code%]", function(code) local is_inline = code:match("\n") == nil if is_inline then table.insert(inline_codes, code) return "\1ICODE:"..#inline_codes.."\1" else -- strip leading and trailing newlines, preserve others local m, _ = code:gsub("^%s*(.-)%s*$", "%1") table.insert(code_blocks, m) return "\1CODE:"..#code_blocks.."\1" end end) -- replace `[url=https://example.com]Example[/url] tags text = text:gsub("%[url=([^%]]+)%](.-)%[/url%]", function(url, label) return ''..label..'' end) -- replace `[url]https://example.com[/url] tags text = text:gsub("%[url%]([^%]]+)%[/url%]", function(url) return ''..url..'' end) -- bold, italics, strikethrough text = text:gsub("%[b%](.-)%[/b%]", "%1") text = text:gsub("%[i%](.-)%[/i%]", "%1") text = text:gsub("%[s%](.-)%[/s%]", "%1") -- replace loose links text = text:gsub("(https?://[%w-_%.%?%.:/%+=&~%@#%%]+[%w-/])", function(url) if not text:find(']*>'..url..'') then return ''..url..'' end return url end) -- rule text = text:gsub("\n+%-%-%-", "
") -- normalize newlines, replace them with
text = text:gsub("\r?\n\r?\n+", "
")--:gsub("\r?\n", "
") -- replace code block placeholders back with their original contents text = text:gsub("\1CODE:(%d+)\1", function(n) local code = code_blocks[tonumber(n)] local button = (""):format(code) return "
" .. button .. ""..code.."
" end) text = text:gsub("\1ICODE:(%d+)\1", function (n) local code = inline_codes[tonumber(n)] return "" .. code .. "" end) return text end return babycode