diff options
author | Karl Berry <karl@freefriends.org> | 2022-12-23 21:03:50 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2022-12-23 21:03:50 +0000 |
commit | 8d383d484835575910b71ef5e1746d57be9afef3 (patch) | |
tree | 5d61e2067af838ba094af5faed2682ca07373ff1 /Master/texmf-dist/tex/luatex | |
parent | 23f484c87d10a1d39e2ea85bab67f04e17adff4a (diff) |
markdown (23dec22)
git-svn-id: svn://tug.org/texlive/trunk@65339 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
-rw-r--r-- | Master/texmf-dist/tex/luatex/markdown/markdown.lua | 909 |
1 files changed, 542 insertions, 367 deletions
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua index 3afe0a924d4..8a5a6ce9f56 100644 --- a/Master/texmf-dist/tex/luatex/markdown/markdown.lua +++ b/Master/texmf-dist/tex/luatex/markdown/markdown.lua @@ -58,7 +58,7 @@ -- those in the standard .ins files. -- local metadata = { - version = "2.18.0-0-gd8ae860", + version = "2.19.0-0-g80fcf20", comment = "A module for the conversion from markdown to plain TeX", author = "John MacFarlane, Hans Hagen, Vít Novotný", copyright = {"2009-2016 John MacFarlane, Hans Hagen", @@ -118,7 +118,9 @@ defaultOptions.debugExtensionsFileName = "debug-extensions.json" defaultOptions.frozenCacheFileName = "frozenCache.tex" defaultOptions.blankBeforeBlockquote = false defaultOptions.blankBeforeCodeFence = false +defaultOptions.blankBeforeDivFence = false defaultOptions.blankBeforeHeading = false +defaultOptions.bracketedSpans = false defaultOptions.breakableBlockquotes = false defaultOptions.citationNbsps = true defaultOptions.citations = false @@ -127,15 +129,14 @@ defaultOptions.contentBlocks = false defaultOptions.debugExtensions = false defaultOptions.definitionLists = false defaultOptions.eagerCache = true +defaultOptions.expectJekyllData = false metadata.user_extension_api_version = 2 metadata.grammar_version = 2 defaultOptions.extensions = {} -defaultOptions.expectJekyllData = false defaultOptions.fancyLists = false defaultOptions.fencedCode = false +defaultOptions.fencedDivs = false defaultOptions.finalizeCache = false -defaultOptions.footnotes = false -defaultOptions.notes = false defaultOptions.frozenCacheCounter = 0 defaultOptions.hardLineBreaks = false defaultOptions.hashEnumerators = false @@ -145,6 +146,8 @@ defaultOptions.hybrid = false defaultOptions.inlineFootnotes = false defaultOptions.inlineNotes = false defaultOptions.jekyllData = false +defaultOptions.footnotes = false +defaultOptions.notes = false defaultOptions.pipeTables = false defaultOptions.preserveTabs = false defaultOptions.rawAttribute = true @@ -2423,7 +2426,6 @@ function M.writer.new(options) self.escaped_uri_chars = { ["{"] = "\\markdownRendererLeftBrace{}", ["}"] = "\\markdownRendererRightBrace{}", - ["%"] = "\\markdownRendererPercentSign{}", ["\\"] = "\\markdownRendererBackslash{}", } self.escaped_minimal_strings = { @@ -2577,6 +2579,27 @@ function M.writer.new(options) return buf end + function self.attributes(attr) + local buf = {} + + table.sort(attr) + local key, value + for i = 1, #attr do + if attr[i]:sub(1, 1) == "#" then + table.insert(buf, {"\\markdownRendererAttributeIdentifier{", + attr[i]:sub(2), "}"}) + elseif attr[i]:sub(1, 1) == "." then + table.insert(buf, {"\\markdownRendererAttributeClassName{", + attr[i]:sub(2), "}"}) + else + key, value = attr[i]:match("([^= ]+)%s*=%s*(.*)") + table.insert(buf, {"\\markdownRendererAttributeKeyValue{", + key, "}{", value, "}"}) + end + end + + return buf + end self.active_attributes = {} function self.heading(s, level, attributes) attributes = attributes or {} @@ -2655,21 +2678,7 @@ function M.writer.new(options) end if self.is_writing then - table.sort(attributes) - local key, value - for i = 1, #attributes do - if attributes[i]:sub(1, 1) == "#" then - table.insert(buf, {"\\markdownRendererAttributeIdentifier{", - attributes[i]:sub(2), "}"}) - elseif attributes[i]:sub(1, 1) == "." then - table.insert(buf, {"\\markdownRendererAttributeClassName{", - attributes[i]:sub(2), "}"}) - else - key, value = attributes[i]:match("([^= ]+)%s*=%s*(.*)") - table.insert(buf, {"\\markdownRendererAttributeKeyValue{", - key, "}{", value, "}"}) - end - end + table.insert(buf, self.attributes(attributes)) end local cmd @@ -2770,7 +2779,8 @@ parsers.doubletildes = P("~~") parsers.fourspaces = P(" ") parsers.any = P(1) -parsers.fail = parsers.any - 1 +parsers.succeed = P(true) +parsers.fail = P(false) parsers.escapable = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") parsers.anyescaped = parsers.backslash / "" * parsers.escapable @@ -2922,14 +2932,31 @@ local function captures_geq_length(_,i,a,b) return #a >= #b and i end -parsers.infostring = (parsers.linechar - (parsers.backtick - + parsers.space^1 * (parsers.newline + parsers.eof)))^0 +parsers.tilde_infostring + = C((parsers.linechar + - (parsers.spacechar^1 * parsers.newline))^0) + * parsers.optionalspace + * (parsers.newline + parsers.eof) + +parsers.backtick_infostring + = C((parsers.linechar + - (parsers.backtick + + parsers.spacechar^1 * parsers.newline))^0) + * parsers.optionalspace + * (parsers.newline + parsers.eof) local fenceindent -parsers.fencehead = function(char) +parsers.fencehead = function(char, infostring) + return C(parsers.nonindentspace) / function(s) fenceindent = #s end + * Cg(char^3, "fencelength") + * parsers.optionalspace * infostring +end + +parsers.fencehead_with_attributes + = function(char) return C(parsers.nonindentspace) / function(s) fenceindent = #s end * Cg(char^3, "fencelength") - * parsers.optionalspace * C(parsers.infostring) + * parsers.optionalspace * Ct(parsers.attributes) * parsers.optionalspace * (parsers.newline + parsers.eof) end @@ -3145,12 +3172,14 @@ end parsers.urlchar = parsers.anyescaped - parsers.newline - parsers.more parsers.TildeFencedCode - = parsers.fencehead(parsers.tilde) + = parsers.fencehead(parsers.tilde, + parsers.tilde_infostring) * Cs(parsers.fencedline(parsers.tilde)^0) * parsers.fencetail(parsers.tilde) parsers.BacktickFencedCode - = parsers.fencehead(parsers.backtick) + = parsers.fencehead(parsers.backtick, + parsers.backtick_infostring) * Cs(parsers.fencedline(parsers.backtick)^0) * parsers.fencetail(parsers.backtick) @@ -3698,12 +3727,12 @@ function M.reader.new(writer, options) local syntax = { "Blocks", - Blocks = ( V("ExpectedJekyllData") - * (V("Blank")^0 / writer.interblocksep) - )^-1 + Blocks = V("InitializeState") + * ( V("ExpectedJekyllData") + * (V("Blank")^0 / writer.interblocksep))^-1 * V("Blank")^0 * V("Block")^-1 - * (V("Blank")^0 / writer.interblocksep + * ( V("Blank")^0 / writer.interblocksep * V("Block"))^0 * V("Blank")^0 * parsers.eof, @@ -3742,12 +3771,24 @@ function M.reader.new(writer, options) Smart = parsers.Smart, Symbol = parsers.Symbol, SpecialChar = parsers.fail, + InitializeState = parsers.succeed, } - self.update_rule = function(rule_name, pattern) + self.update_rule = function(rule_name, get_pattern) assert(current_extension_name ~= nil) assert(syntax[rule_name] ~= nil, [[Rule ]] .. rule_name .. [[ -> ... does not exist in markdown grammar]]) - local accountable_pattern = { pattern, current_extension_name, rule_name } + local previous_pattern + local extension_name + if walkable_syntax[rule_name] then + local previous_accountable_pattern = walkable_syntax[rule_name][1] + previous_pattern = previous_accountable_pattern[1] + extension_name = previous_accountable_pattern[2] .. ", " .. current_extension_name + else + previous_pattern = nil + extension_name = current_extension_name + end + local pattern = get_pattern(previous_pattern) + local accountable_pattern = { pattern, extension_name, rule_name } walkable_syntax[rule_name] = { accountable_pattern } end local special_characters = {} @@ -3763,6 +3804,10 @@ function M.reader.new(writer, options) self.add_special_character("<") self.add_special_character("!") self.add_special_character("\\") + self.initialize_named_group = function(name, value) + syntax.InitializeState = syntax.InitializeState + * Cg(Ct("") / value, name) + end for _, extension in ipairs(extensions) do current_extension_name = extension.name extension.extend_writer(writer) @@ -3875,7 +3920,10 @@ function M.reader.new(writer, options) local inlines_t = util.table_copy(syntax) inlines_t[1] = "Inlines" - inlines_t.Inlines = parsers.Inline^0 * (parsers.spacing^0 * parsers.eof / "") + inlines_t.Inlines = V("InitializeState") + * parsers.Inline^0 + * ( parsers.spacing^0 + * parsers.eof / "") parsers.inlines = Ct(inlines_t) local inlines_no_link_t = util.table_copy(inlines_t) @@ -3944,6 +3992,31 @@ function M.reader.new(writer, options) return self end M.extensions = {} +M.extensions.bracketed_spans = function() + return { + name = "built-in bracketed_spans syntax extension", + extend_writer = function(self) + function self.span(s, attr) + return {"\\markdownRendererBracketedSpanAttributeContextBegin", + self.attributes(attr), + s, + "\\markdownRendererBracketedSpanAttributeContextEnd{}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer + + local Span = parsers.between(parsers.Inline, + parsers.lbracket, + parsers.rbracket) + * Ct(parsers.attributes) + / writer.span + + self.insert_pattern("Inline after Emph", + Span, "Span") + end + } +end M.extensions.citations = function(citation_nbsps) local escaped_citation_chars = { ["{"] = "\\markdownRendererLeftBrace{}", @@ -4296,6 +4369,163 @@ M.extensions.definition_lists = function(tight_lists) end } end +M.extensions.fancy_lists = function() + return { + name = "built-in fancy_lists syntax extension", + extend_writer = function(self) + local options = self.options + + function self.fancylist(items,tight,startnum,numstyle,numdelim) + if not self.is_writing then return "" end + local buffer = {} + local num = startnum + for _,item in ipairs(items) do + buffer[#buffer + 1] = self.fancyitem(item,num) + if num ~= nil then + num = num + 1 + end + end + local contents = util.intersperse(buffer,"\n") + if tight and options.tightLists then + return {"\\markdownRendererFancyOlBeginTight{", + numstyle,"}{",numdelim,"}",contents, + "\n\\markdownRendererFancyOlEndTight "} + else + return {"\\markdownRendererFancyOlBegin{", + numstyle,"}{",numdelim,"}",contents, + "\n\\markdownRendererFancyOlEnd "} + end + end + function self.fancyitem(s,num) + if num ~= nil then + return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s, + "\\markdownRendererFancyOlItemEnd "} + else + return {"\\markdownRendererFancyOlItem ",s,"\\markdownRendererFancyOlItemEnd "} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local options = self.options + local writer = self.writer + + local label = parsers.dig + parsers.letter + local numdelim = parsers.period + parsers.rparent + local enumerator = C(label^3 * numdelim) * #parsers.spacing + + C(label^2 * numdelim) * #parsers.spacing + * (parsers.tab + parsers.space^1) + + C(label * numdelim) * #parsers.spacing + * (parsers.tab + parsers.space^-2) + + parsers.space * C(label^2 * numdelim) + * #parsers.spacing + + parsers.space * C(label * numdelim) + * #parsers.spacing + * (parsers.tab + parsers.space^-1) + + parsers.space * parsers.space * C(label^1 + * numdelim) * #parsers.spacing + local starter = parsers.bullet + enumerator + + local NestedList = Cs((parsers.optionallyindentedline + - starter)^1) + / function(a) return "\001"..a end + + local ListBlockLine = parsers.optionallyindentedline + - parsers.blankline - (parsers.indent^-1 + * starter) + + local ListBlock = parsers.line * ListBlockLine^0 + + local ListContinuationBlock = parsers.blanklines * (parsers.indent / "") + * ListBlock + + local TightListItem = function(starter) + return -parsers.ThematicBreak + * (Cs(starter / "" * parsers.tickbox^-1 * ListBlock * NestedList^-1) + / self.parser_functions.parse_blocks_nested) + * -(parsers.blanklines * parsers.indent) + end + + local LooseListItem = function(starter) + return -parsers.ThematicBreak + * Cs( starter / "" * parsers.tickbox^-1 * ListBlock * Cc("\n") + * (NestedList + ListContinuationBlock^0) + * (parsers.blanklines / "\n\n") + ) / self.parser_functions.parse_blocks_nested + end + + local function roman2number(roman) + local romans = { ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 } + local numeral = 0 + + local i = 1 + local len = string.len(roman) + while i < len do + local z1, z2 = romans[ string.sub(roman, i, i) ], romans[ string.sub(roman, i+1, i+1) ] + if z1 < z2 then + numeral = numeral + (z2 - z1) + i = i + 2 + else + numeral = numeral + z1 + i = i + 1 + end + end + if i <= len then numeral = numeral + romans[ string.sub(roman,i,i) ] end + return numeral + end + + local function sniffstyle(itemprefix) + local numstr, delimend = itemprefix:match("^([A-Za-z0-9]*)([.)]*)") + local numdelim + if delimend == ")" then + numdelim = "OneParen" + elseif delimend == "." then + numdelim = "Period" + else + numdelim = "Default" + end + numstr = numstr or itemprefix + + local num + num = numstr:match("^([IVXL]+)") + if num then + return roman2number(num), "UpperRoman", numdelim + end + num = numstr:match("^([ivxl]+)") + if num then + return roman2number(string.upper(num)), "LowerRoman", numdelim + end + num = numstr:match("^([A-Z])") + if num then + return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim + end + num = numstr:match("^([a-z])") + if num then + return string.byte(num) - string.byte("a") + 1, "LowerAlpha", numdelim + end + return math.floor(tonumber(numstr) or 1), "Decimal", numdelim + end + + local function fancylist(items,tight,start) + local startnum, numstyle, numdelim = sniffstyle(start) + return writer.fancylist(items,tight, + options.startNumber and startnum, + numstyle or "Decimal", + numdelim or "Default") + end + + local FancyList = Cg(enumerator, "listtype") * + ( Ct(TightListItem(Cb("listtype")) + * TightListItem(enumerator)^0) + * Cc(true) * parsers.skipblanklines * -enumerator + + Ct(LooseListItem(Cb("listtype")) + * LooseListItem(enumerator)^0) + * Cc(false) * parsers.skipblanklines + ) * Cb("listtype") / fancylist + + self.update_rule("OrderedList", function() return FancyList end) + end + } +end M.extensions.fenced_code = function(blank_before_code_fence) return { name = "built-in fenced_code syntax extension", @@ -4313,12 +4543,12 @@ M.extensions.fenced_code = function(blank_before_code_fence) local writer = self.writer local FencedCode = (parsers.TildeFencedCode - + parsers.BacktickFencedCode) - / function(infostring, code) - local expanded_code = self.expandtabs(code) - return writer.fencedCode(expanded_code, - infostring) - end + + parsers.BacktickFencedCode) + / function(infostring, code) + local expanded_code = self.expandtabs(code) + return writer.fencedCode(expanded_code, + infostring) + end self.insert_pattern("Block after Verbatim", FencedCode, "FencedCode") @@ -4327,83 +4557,105 @@ M.extensions.fenced_code = function(blank_before_code_fence) if blank_before_code_fence then fencestart = parsers.fail else - fencestart = parsers.fencehead(parsers.backtick) - + parsers.fencehead(parsers.tilde) + fencestart = parsers.fencehead(parsers.backtick, + parsers.backtick_infostring) + + parsers.fencehead(parsers.tilde, + parsers.tilde_infostring) end - local EndlineExceptions = parsers.EndlineExceptions + fencestart - self.update_rule("EndlineExceptions", EndlineExceptions) + self.update_rule("EndlineExceptions", function(previous_pattern) + if previous_pattern == nil then + previous_pattern = parsers.EndlineExceptions + end + return previous_pattern + fencestart + end) self.add_special_character("~") end } end -M.extensions.notes = function(notes, inline_notes) - assert(notes or inline_notes) +M.extensions.fenced_divs = function(blank_before_div_fence) return { - name = "built-in notes syntax extension", + name = "built-in fenced_divs syntax extension", extend_writer = function(self) - function self.note(s) - return {"\\markdownRendererNote{",s,"}"} + function self.div(c, attr) + return {"\\markdownRendererFencedDivAttributeContextBegin", + self.attributes(attr), + c, + "\\markdownRendererFencedDivAttributeContextEnd"} end end, extend_reader = function(self) local parsers = self.parsers local writer = self.writer - - if inline_notes then - local InlineNote - = parsers.circumflex - * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note) - / writer.note - - self.insert_pattern("Inline after Emph", - InlineNote, "InlineNote") - end - if notes then - local function strip_first_char(s) - return s:sub(2) + local fenced_div_infostring + = C((parsers.linechar + - ( parsers.spacechar^1 + * parsers.colon^1))^1) + + local fenced_div_begin = parsers.nonindentspace + * parsers.colon^3 + * parsers.optionalspace + * fenced_div_infostring + * ( parsers.spacechar^1 + * parsers.colon^1)^0 + * parsers.optionalspace + * (parsers.newline + parsers.eof) + + local fenced_div_end = parsers.nonindentspace + * parsers.colon^3 + * parsers.optionalspace + * (parsers.newline + parsers.eof) + self.initialize_named_group("div_level", "0") + + local function increment_div_level(increment) + local function update_div_level(s, i, current_level) -- luacheck: ignore s i + current_level = tonumber(current_level) + local next_level = tostring(current_level + increment) + return true, next_level end - local RawNoteRef - = #(parsers.lbracket * parsers.circumflex) - * parsers.tag / strip_first_char + return Cg( Cmt(Cb("div_level"), update_div_level) + , "div_level") + end - local rawnotes = {} + local FencedDiv = fenced_div_begin * increment_div_level(1) + * parsers.skipblanklines + * Ct( (V("Block") - fenced_div_end)^-1 + * ( parsers.blanklines + / function() + return writer.interblocksep + end + * (V("Block") - fenced_div_end))^0) + * parsers.skipblanklines + * fenced_div_end * increment_div_level(-1) + / function (infostring, div) + local attr = lpeg.match(Ct(parsers.attributes), infostring) + if attr == nil then + attr = {"." .. infostring} + end + return div, attr + end + / writer.div - -- like indirect_link - local function lookup_note(ref) - return writer.defer_call(function() - local found = rawnotes[self.normalize_tag(ref)] - if found then - return writer.note( - self.parser_functions.parse_blocks_nested(found)) - else - return {"[", - self.parser_functions.parse_inlines("^" .. ref), "]"} - end - end) - end + self.insert_pattern("Block after Verbatim", + FencedDiv, "FencedDiv") - local function register_note(ref,rawnote) - rawnotes[self.normalize_tag(ref)] = rawnote - return "" + self.add_special_character(":") + if not blank_before_div_fence then + local function check_div_level(s, i, current_level) -- luacheck: ignore s i + current_level = tonumber(current_level) + return current_level > 0 end - local NoteRef = RawNoteRef / lookup_note - - local NoteBlock - = parsers.leader * RawNoteRef * parsers.colon - * parsers.spnl * parsers.indented_blocks(parsers.chunk) - / register_note - - local Blank = NoteBlock + parsers.Blank - self.update_rule("Blank", Blank) - - self.insert_pattern("Inline after Emph", - NoteRef, "NoteRef") + local is_inside_div = Cmt(Cb("div_level"), check_div_level) + local fencestart = is_inside_div * fenced_div_end + self.update_rule("EndlineExceptions", function(previous_pattern) + if previous_pattern == nil then + previous_pattern = parsers.EndlineExceptions + end + return previous_pattern + fencestart + end) end - - self.add_special_character("^") end } end @@ -4458,138 +4710,76 @@ M.extensions.header_attributes = function() / writer.heading local Heading = AtxHeading + SetextHeading - self.update_rule("Heading", Heading) + self.update_rule("Heading", function() return Heading end) end } end -M.extensions.jekyll_data = function(expect_jekyll_data) +M.extensions.notes = function(notes, inline_notes) + assert(notes or inline_notes) return { - name = "built-in jekyll_data syntax extension", + name = "built-in notes syntax extension", extend_writer = function(self) - function self.jekyllData(d, t, p) - if not self.is_writing then return "" end + function self.note(s) + return {"\\markdownRendererNote{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer - local buf = {} + if inline_notes then + local InlineNote + = parsers.circumflex + * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note) + / writer.note - local keys = {} - for k, _ in pairs(d) do - table.insert(keys, k) + self.insert_pattern("Inline after Emph", + InlineNote, "InlineNote") + end + if notes then + local function strip_first_char(s) + return s:sub(2) end - table.sort(keys) - if not p then - table.insert(buf, "\\markdownRendererJekyllDataBegin") - end + local RawNoteRef + = #(parsers.lbracket * parsers.circumflex) + * parsers.tag / strip_first_char - if #d > 0 then - table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{") - table.insert(buf, self.uri(p or "null")) - table.insert(buf, "}{") - table.insert(buf, #keys) - table.insert(buf, "}") - else - table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{") - table.insert(buf, self.uri(p or "null")) - table.insert(buf, "}{") - table.insert(buf, #keys) - table.insert(buf, "}") - end + local rawnotes = {} - for _, k in ipairs(keys) do - local v = d[k] - local typ = type(v) - k = tostring(k or "null") - if typ == "table" and next(v) ~= nil then - table.insert( - buf, - self.jekyllData(v, t, k) - ) - else - k = self.uri(k) - v = tostring(v) - if typ == "boolean" then - table.insert(buf, "\\markdownRendererJekyllDataBoolean{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, v) - table.insert(buf, "}") - elseif typ == "number" then - table.insert(buf, "\\markdownRendererJekyllDataNumber{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, v) - table.insert(buf, "}") - elseif typ == "string" then - table.insert(buf, "\\markdownRendererJekyllDataString{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, t(v)) - table.insert(buf, "}") - elseif typ == "table" then - table.insert(buf, "\\markdownRendererJekyllDataEmpty{") - table.insert(buf, k) - table.insert(buf, "}") + -- like indirect_link + local function lookup_note(ref) + return writer.defer_call(function() + local found = rawnotes[self.normalize_tag(ref)] + if found then + return writer.note( + self.parser_functions.parse_blocks_nested(found)) else - error(format("Unexpected type %s for value of " .. - "YAML key %s", typ, k)) + return {"[", + self.parser_functions.parse_inlines("^" .. ref), "]"} end - end - end - - if #d > 0 then - table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd") - else - table.insert(buf, "\\markdownRendererJekyllDataMappingEnd") + end) end - if not p then - table.insert(buf, "\\markdownRendererJekyllDataEnd") + local function register_note(ref,rawnote) + rawnotes[self.normalize_tag(ref)] = rawnote + return "" end - return buf - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local JekyllData - = Cmt( C((parsers.line - P("---") - P("..."))^0) - , function(s, i, text) -- luacheck: ignore s i - local data - local ran_ok, _ = pcall(function() - local tinyyaml = require("markdown-tinyyaml") - data = tinyyaml.parse(text, {timestamps=false}) - end) - if ran_ok and data ~= nil then - return true, writer.jekyllData(data, function(s) - return self.parser_functions.parse_blocks_nested(s) - end, nil) - else - return false - end - end - ) + local NoteRef = RawNoteRef / lookup_note - local UnexpectedJekyllData - = P("---") - * parsers.blankline / 0 - * #(-parsers.blankline) -- if followed by blank, it's thematic break - * JekyllData - * (P("---") + P("...")) + local NoteBlock + = parsers.leader * RawNoteRef * parsers.colon + * parsers.spnl * parsers.indented_blocks(parsers.chunk) + / register_note - local ExpectedJekyllData - = ( P("---") - * parsers.blankline / 0 - * #(-parsers.blankline) -- if followed by blank, it's thematic break - )^-1 - * JekyllData - * (P("---") + P("..."))^-1 + local Blank = NoteBlock + parsers.Blank + self.update_rule("Blank", function() return Blank end) - self.insert_pattern("Block before Blockquote", - UnexpectedJekyllData, "UnexpectedJekyllData") - if expect_jekyll_data then - self.update_rule("ExpectedJekyllData", ExpectedJekyllData) + self.insert_pattern("Inline after Emph", + NoteRef, "NoteRef") end + + self.add_special_character("^") end } end @@ -4775,17 +4965,17 @@ M.extensions.raw_attribute = function() if options.fencedCode then local RawBlock = (parsers.TildeFencedCode - + parsers.BacktickFencedCode) - / function(infostring, code) - local expanded_code = self.expandtabs(code) - local attr = lpeg.match(raw_attribute, infostring) - if attr then - return writer.rawBlock(expanded_code, attr) - else - return writer.fencedCode(expanded_code, - infostring) - end + + parsers.BacktickFencedCode) + / function(infostring, code) + local expanded_code = self.expandtabs(code) + local attr = lpeg.match(raw_attribute, infostring) + if attr then + return writer.rawBlock(expanded_code, attr) + else + return writer.fencedCode(expanded_code, + infostring) end + end self.insert_pattern("Block after Verbatim", RawBlock, "RawBlock") @@ -4816,28 +5006,6 @@ M.extensions.strike_through = function() end } end -M.extensions.superscripts = function() - return { - name = "built-in superscripts syntax extension", - extend_writer = function(self) - function self.superscript(s) - return {"\\markdownRendererSuperscript{",s,"}"} - end - end, extend_reader = function(self) - local parsers = self.parsers - local writer = self.writer - - local Superscript = ( - parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex) - ) / writer.superscript - - self.insert_pattern("Inline after Emph", - Superscript, "Superscript") - - self.add_special_character("^") - end - } -end M.extensions.subscripts = function() return { name = "built-in subscripts syntax extension", @@ -4860,160 +5028,156 @@ M.extensions.subscripts = function() end } end -M.extensions.fancy_lists = function() +M.extensions.superscripts = function() return { - name = "built-in fancy_lists syntax extension", + name = "built-in superscripts syntax extension", extend_writer = function(self) - local options = self.options - - function self.fancylist(items,tight,startnum,numstyle,numdelim) - if not self.is_writing then return "" end - local buffer = {} - local num = startnum - for _,item in ipairs(items) do - buffer[#buffer + 1] = self.fancyitem(item,num) - if num ~= nil then - num = num + 1 - end - end - local contents = util.intersperse(buffer,"\n") - if tight and options.tightLists then - return {"\\markdownRendererFancyOlBeginTight{", - numstyle,"}{",numdelim,"}",contents, - "\n\\markdownRendererFancyOlEndTight "} - else - return {"\\markdownRendererFancyOlBegin{", - numstyle,"}{",numdelim,"}",contents, - "\n\\markdownRendererFancyOlEnd "} - end - end - function self.fancyitem(s,num) - if num ~= nil then - return {"\\markdownRendererFancyOlItemWithNumber{",num,"}",s, - "\\markdownRendererFancyOlItemEnd "} - else - return {"\\markdownRendererFancyOlItem ",s,"\\markdownRendererFancyOlItemEnd "} - end + function self.superscript(s) + return {"\\markdownRendererSuperscript{",s,"}"} end end, extend_reader = function(self) local parsers = self.parsers - local options = self.options local writer = self.writer - local label = parsers.dig + parsers.letter - local numdelim = parsers.period + parsers.rparent - local enumerator = C(label^3 * numdelim) * #parsers.spacing - + C(label^2 * numdelim) * #parsers.spacing - * (parsers.tab + parsers.space^1) - + C(label * numdelim) * #parsers.spacing - * (parsers.tab + parsers.space^-2) - + parsers.space * C(label^2 * numdelim) - * #parsers.spacing - + parsers.space * C(label * numdelim) - * #parsers.spacing - * (parsers.tab + parsers.space^-1) - + parsers.space * parsers.space * C(label^1 - * numdelim) * #parsers.spacing - local starter = parsers.bullet + enumerator - - local NestedList = Cs((parsers.optionallyindentedline - - starter)^1) - / function(a) return "\001"..a end + local Superscript = ( + parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex) + ) / writer.superscript - local ListBlockLine = parsers.optionallyindentedline - - parsers.blankline - (parsers.indent^-1 - * starter) + self.insert_pattern("Inline after Emph", + Superscript, "Superscript") - local ListBlock = parsers.line * ListBlockLine^0 + self.add_special_character("^") + end + } +end +M.extensions.jekyll_data = function(expect_jekyll_data) + return { + name = "built-in jekyll_data syntax extension", + extend_writer = function(self) + function self.jekyllData(d, t, p) + if not self.is_writing then return "" end - local ListContinuationBlock = parsers.blanklines * (parsers.indent / "") - * ListBlock + local buf = {} - local TightListItem = function(starter) - return -parsers.ThematicBreak - * (Cs(starter / "" * parsers.tickbox^-1 * ListBlock * NestedList^-1) - / self.parser_functions.parse_blocks_nested) - * -(parsers.blanklines * parsers.indent) - end + local keys = {} + for k, _ in pairs(d) do + table.insert(keys, k) + end + table.sort(keys) - local LooseListItem = function(starter) - return -parsers.ThematicBreak - * Cs( starter / "" * parsers.tickbox^-1 * ListBlock * Cc("\n") - * (NestedList + ListContinuationBlock^0) - * (parsers.blanklines / "\n\n") - ) / self.parser_functions.parse_blocks_nested - end + if not p then + table.insert(buf, "\\markdownRendererJekyllDataBegin") + end - local function roman2number(roman) - local romans = { ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 } - local numeral = 0 + if #d > 0 then + table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{") + table.insert(buf, self.uri(p or "null")) + table.insert(buf, "}{") + table.insert(buf, #keys) + table.insert(buf, "}") + else + table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{") + table.insert(buf, self.uri(p or "null")) + table.insert(buf, "}{") + table.insert(buf, #keys) + table.insert(buf, "}") + end - local i = 1 - local len = string.len(roman) - while i < len do - local z1, z2 = romans[ string.sub(roman, i, i) ], romans[ string.sub(roman, i+1, i+1) ] - if z1 < z2 then - numeral = numeral + (z2 - z1) - i = i + 2 + for _, k in ipairs(keys) do + local v = d[k] + local typ = type(v) + k = tostring(k or "null") + if typ == "table" and next(v) ~= nil then + table.insert( + buf, + self.jekyllData(v, t, k) + ) else - numeral = numeral + z1 - i = i + 1 + k = self.uri(k) + v = tostring(v) + if typ == "boolean" then + table.insert(buf, "\\markdownRendererJekyllDataBoolean{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, v) + table.insert(buf, "}") + elseif typ == "number" then + table.insert(buf, "\\markdownRendererJekyllDataNumber{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, v) + table.insert(buf, "}") + elseif typ == "string" then + table.insert(buf, "\\markdownRendererJekyllDataString{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, t(v)) + table.insert(buf, "}") + elseif typ == "table" then + table.insert(buf, "\\markdownRendererJekyllDataEmpty{") + table.insert(buf, k) + table.insert(buf, "}") + else + error(format("Unexpected type %s for value of " .. + "YAML key %s", typ, k)) + end end end - if i <= len then numeral = numeral + romans[ string.sub(roman,i,i) ] end - return numeral - end - local function sniffstyle(itemprefix) - local numstr, delimend = itemprefix:match("^([A-Za-z0-9]*)([.)]*)") - local numdelim - if delimend == ")" then - numdelim = "OneParen" - elseif delimend == "." then - numdelim = "Period" + if #d > 0 then + table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd") else - numdelim = "Default" + table.insert(buf, "\\markdownRendererJekyllDataMappingEnd") end - numstr = numstr or itemprefix - local num - num = numstr:match("^([IVXL]+)") - if num then - return roman2number(num), "UpperRoman", numdelim - end - num = numstr:match("^([ivxl]+)") - if num then - return roman2number(string.upper(num)), "LowerRoman", numdelim - end - num = numstr:match("^([A-Z])") - if num then - return string.byte(num) - string.byte("A") + 1, "UpperAlpha", numdelim - end - num = numstr:match("^([a-z])") - if num then - return string.byte(num) - string.byte("a") + 1, "LowerAlpha", numdelim + if not p then + table.insert(buf, "\\markdownRendererJekyllDataEnd") end - return math.floor(tonumber(numstr) or 1), "Decimal", numdelim - end - local function fancylist(items,tight,start) - local startnum, numstyle, numdelim = sniffstyle(start) - return writer.fancylist(items,tight, - options.startNumber and startnum, - numstyle or "Decimal", - numdelim or "Default") + return buf end + end, extend_reader = function(self) + local parsers = self.parsers + local writer = self.writer - local FancyList = Cg(enumerator, "listtype") * - ( Ct(TightListItem(Cb("listtype")) - * TightListItem(enumerator)^0) - * Cc(true) * parsers.skipblanklines * -enumerator - + Ct(LooseListItem(Cb("listtype")) - * LooseListItem(enumerator)^0) - * Cc(false) * parsers.skipblanklines - ) * Cb("listtype") / fancylist + local JekyllData + = Cmt( C((parsers.line - P("---") - P("..."))^0) + , function(s, i, text) -- luacheck: ignore s i + local data + local ran_ok, _ = pcall(function() + local tinyyaml = require("markdown-tinyyaml") + data = tinyyaml.parse(text, {timestamps=false}) + end) + if ran_ok and data ~= nil then + return true, writer.jekyllData(data, function(s) + return self.parser_functions.parse_blocks_nested(s) + end, nil) + else + return false + end + end + ) - self.update_rule("OrderedList", FancyList) + local UnexpectedJekyllData + = P("---") + * parsers.blankline / 0 + * #(-parsers.blankline) -- if followed by blank, it's thematic break + * JekyllData + * (P("---") + P("...")) + + local ExpectedJekyllData + = ( P("---") + * parsers.blankline / 0 + * #(-parsers.blankline) -- if followed by blank, it's thematic break + )^-1 + * JekyllData + * (P("---") + P("..."))^-1 + + self.insert_pattern("Block before Blockquote", + UnexpectedJekyllData, "UnexpectedJekyllData") + if expect_jekyll_data then + self.update_rule("ExpectedJekyllData", function() return ExpectedJekyllData end) + end end } end @@ -5023,6 +5187,11 @@ function M.new(options) return defaultOptions[key] end }) local extensions = {} + if options.bracketedSpans then + local bracketed_spans_extension = M.extensions.bracketed_spans() + table.insert(extensions, bracketed_spans_extension) + end + if options.contentBlocks then local content_blocks_extension = M.extensions.content_blocks( options.contentBlocksLanguageMap) @@ -5041,6 +5210,12 @@ function M.new(options) table.insert(extensions, fenced_code_extension) end + if options.fencedDivs then + local fenced_div_extension = M.extensions.fenced_divs( + options.blankBeforeDivFence) + table.insert(extensions, fenced_div_extension) + end + if options.headerAttributes then local header_attributes_extension = M.extensions.header_attributes() table.insert(extensions, header_attributes_extension) |