diff options
author | Karl Berry <karl@freefriends.org> | 2022-08-26 19:56:57 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2022-08-26 19:56:57 +0000 |
commit | 09bced275b43579d54d22b41a416382b2a0cdb80 (patch) | |
tree | 7237a85adc00ef83cee55e139b445fff3398dc6a /Master/texmf-dist/tex/luatex/markdown/markdown.lua | |
parent | 514aac2f29abee64350e7f44a1454cbed42e1680 (diff) |
markdown (26aug22)
git-svn-id: svn://tug.org/texlive/trunk@64200 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex/markdown/markdown.lua')
-rw-r--r-- | Master/texmf-dist/tex/luatex/markdown/markdown.lua | 370 |
1 files changed, 314 insertions, 56 deletions
diff --git a/Master/texmf-dist/tex/luatex/markdown/markdown.lua b/Master/texmf-dist/tex/luatex/markdown/markdown.lua index 8e5d29f0133..091e7052bb9 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.15.4-0-g4cbe4e3", + version = "2.16.0-5-g5bb83fb", 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", @@ -94,6 +94,7 @@ defaultOptions.contentBlocksLanguageMap = "markdown-languages.json" defaultOptions.definitionLists = false defaultOptions.eagerCache = true defaultOptions.expectJekyllData = false +defaultOptions.fancyLists = false defaultOptions.fencedCode = false defaultOptions.finalizeCache = false defaultOptions.footnotes = false @@ -112,7 +113,10 @@ defaultOptions.shiftHeadings = 0 defaultOptions.slice = "^ $" defaultOptions.smartEllipses = false defaultOptions.startNumber = true +defaultOptions.strikeThrough = false defaultOptions.stripIndent = false +defaultOptions.subscripts = false +defaultOptions.superscripts = false defaultOptions.tableCaptions = false defaultOptions.taskLists = false defaultOptions.texComments = false @@ -2303,8 +2307,7 @@ end M.writer = {} function M.writer.new(options) local self = {} - self.cacheDir = options.cacheDir - self.hybrid = options.hybrid + self.options = options local slice_specifiers = {} for specifier in options.slice:gmatch("[^%s]+") do table.insert(slice_specifiers, specifier) @@ -2403,16 +2406,11 @@ function M.writer.new(options) "{",self.uri(src),"}", "{",self.string(tit or ""),"}"} end - local function ulitem(s) - return {"\\markdownRendererUlItem ",s, - "\\markdownRendererUlItemEnd "} - end - function self.bulletlist(items,tight) if not self.is_writing then return "" end local buffer = {} for _,item in ipairs(items) do - buffer[#buffer + 1] = ulitem(item) + buffer[#buffer + 1] = self.bulletitem(item) end local contents = util.intersperse(buffer,"\n") if tight and options.tightLists then @@ -2423,22 +2421,16 @@ function M.writer.new(options) "\n\\markdownRendererUlEnd "} end end - local function olitem(s,num) - if num ~= nil then - return {"\\markdownRendererOlItemWithNumber{",num,"}",s, - "\\markdownRendererOlItemEnd "} - else - return {"\\markdownRendererOlItem ",s, - "\\markdownRendererOlItemEnd "} - end + function self.bulletitem(s) + return {"\\markdownRendererUlItem ",s, + "\\markdownRendererUlItemEnd "} end - function self.orderedlist(items,tight,startnum) if not self.is_writing then return "" end local buffer = {} local num = startnum for _,item in ipairs(items) do - buffer[#buffer + 1] = olitem(item,num) + buffer[#buffer + 1] = self.ordereditem(item,num) if num ~= nil then num = num + 1 end @@ -2452,6 +2444,15 @@ function M.writer.new(options) "\n\\markdownRendererOlEnd "} end end + function self.ordereditem(s,num) + if num ~= nil then + return {"\\markdownRendererOlItemWithNumber{",num,"}",s, + "\\markdownRendererOlItemEnd "} + else + return {"\\markdownRendererOlItem ",s, + "\\markdownRendererOlItemEnd "} + end + end function self.inline_html_comment(contents) return {"\\markdownRendererInlineHtmlComment{",contents,"}"} end @@ -2465,7 +2466,7 @@ function M.writer.new(options) end function self.block_html_element(s) if not self.is_writing then return "" end - local name = util.cache(self.cacheDir, s, nil, nil, ".verbatim") + local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") return {"\\markdownRendererInputBlockHtmlElement{",name,"}"} end function self.emphasis(s) @@ -2491,7 +2492,7 @@ function M.writer.new(options) function self.verbatim(s) if not self.is_writing then return "" end s = string.gsub(s, '[\r\n%s]*$', '') - local name = util.cache(self.cacheDir, s, nil, nil, ".verbatim") + local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") return {"\\markdownRendererInputVerbatim{",name,"}"} end function self.document(d) @@ -2601,7 +2602,7 @@ function M.writer.new(options) table.insert(buf, {"\\markdownRendererAttributeClassName{", attributes[i]:sub(2), "}"}) else - key, value = attributes[i]:match("(%w+)=(%w+)") + key, value = attributes[i]:match("([^= ]+)%s*=%s*(.*)") table.insert(buf, {"\\markdownRendererAttributeKeyValue{", key, "}{", value, "}"}) end @@ -2702,12 +2703,13 @@ parsers.internal_punctuation = S(":;,.?") parsers.doubleasterisks = P("**") parsers.doubleunderscores = P("__") +parsers.doubletildes = P("~~") parsers.fourspaces = P(" ") parsers.any = P(1) parsers.fail = parsers.any - 1 -parsers.escapable = S("\\`*_{}[]()+_.!<>#-~:^@;") +parsers.escapable = S("!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~") parsers.anyescaped = parsers.backslash / "" * parsers.escapable + parsers.any @@ -2783,21 +2785,15 @@ parsers.commented_line = Cg(Cc(""), "backslashes") parsers.chunk = parsers.line * (parsers.optionallyindentedline - parsers.blankline)^0 -parsers.css_identifier_char = R("AZ", "az", "09") + S("-_") -parsers.css_identifier = (parsers.hash + parsers.period) - * (((parsers.css_identifier_char - - parsers.dash - parsers.digit) - * parsers.css_identifier_char^1) - + (parsers.dash - * (parsers.css_identifier_char - - parsers.digit) - * parsers.css_identifier_char^0)) -parsers.attribute_key_char = parsers.any - parsers.space - - parsers.squote - parsers.dquote - - parsers.more - parsers.slash - - parsers.equal -parsers.attribute_value_char = parsers.any - parsers.space - - parsers.dquote - parsers.more +parsers.attribute_key_char = parsers.alphanumeric + S("_-") +parsers.attribute_key = (parsers.attribute_key_char + - parsers.dash - parsers.digit) + * parsers.attribute_key_char^0 +parsers.attribute_value = ( (parsers.dquote / "") + * (parsers.anyescaped - parsers.dquote)^0 + * (parsers.dquote / "")) + + ( parsers.anyescaped - parsers.dquote - parsers.rbrace + - parsers.space)^0 -- block followed by 0 or more optionally -- indented blocks with first line indented. @@ -2891,9 +2887,10 @@ parsers.leader = parsers.space^-3 -- content in balanced brackets, parentheses, or quotes: parsers.bracketed = P{ parsers.lbracket - * ((parsers.anyescaped - (parsers.lbracket - + parsers.rbracket - + parsers.blankline^2) + * (( parsers.backslash / "" * parsers.rbracket + + parsers.any - (parsers.lbracket + + parsers.rbracket + + parsers.blankline^2) ) + V(1))^0 * parsers.rbracket } @@ -2921,7 +2918,8 @@ parsers.tag = parsers.lbracket * Cs((parsers.alphanumeric^1 + parsers.bracketed + parsers.inticks - + (parsers.anyescaped + + ( parsers.backslash / "" * parsers.rbracket + + parsers.any - (parsers.rbracket + parsers.blankline^2)))^0) * parsers.rbracket @@ -3074,16 +3072,18 @@ parsers.lineof = function(c) * (parsers.newline * parsers.blankline^1 + parsers.newline^-1 * parsers.eof)) end -parsers.heading_attribute = C(parsers.css_identifier) - + C((parsers.attribute_key_char - - parsers.rbrace)^1 - * parsers.equal - * (parsers.attribute_value_char - - parsers.rbrace)^1) +parsers.heading_attribute = (parsers.dash * Cc(".unnumbered")) + + C((parsers.hash + parsers.period) + * parsers.attribute_key) + + Cs( parsers.attribute_key + * parsers.optionalspace * parsers.equal * parsers.optionalspace + * parsers.attribute_value) parsers.HeadingAttributes = parsers.lbrace + * parsers.optionalspace * parsers.heading_attribute * (parsers.spacechar^1 * parsers.heading_attribute)^0 + * parsers.optionalspace * parsers.rbrace -- parse Atx heading start and return level @@ -3100,6 +3100,7 @@ M.reader = {} function M.reader.new(writer, options, extensions) local self = {} self.writer = writer + self.options = options self.parsers = {} (function(parsers) setmetatable(self.parsers, { @@ -3530,16 +3531,16 @@ function M.reader.new(writer, options, extensions) * parsers.skipblanklines ) / writer.bulletlist - local function ordered_list(items,tight,startNumber) + local function ordered_list(items,tight,startnum) if options.startNumber then - startNumber = tonumber(startNumber) or 1 -- fallback for '#' - if startNumber ~= nil then - startNumber = math.floor(startNumber) + startnum = tonumber(startnum) or 1 -- fallback for '#' + if startnum ~= nil then + startnum = math.floor(startnum) end else - startNumber = nil + startnum = nil end - return writer.orderedlist(items,tight,startNumber) + return writer.orderedlist(items,tight,startnum) end parsers.OrderedList = Cg(parsers.enumerator, "listtype") * @@ -3625,6 +3626,9 @@ function M.reader.new(writer, options, extensions) + V("UlOrStarLine") + V("Strong") + V("Emph") + + V("StrikeThrough") + + V("Superscript") + + V("Subscript") + V("InlineNote") + V("NoteRef") + V("Citations") @@ -3646,6 +3650,9 @@ function M.reader.new(writer, options, extensions) + V("UlOrStarLine") + V("Strong") + V("Emph") + + V("StrikeThrough") + + V("Superscript") + + V("Subscript") + V("InlineNote") + V("NoteRef") + V("Citations") @@ -3668,6 +3675,9 @@ function M.reader.new(writer, options, extensions) UlOrStarLine = parsers.UlOrStarLine, Strong = parsers.Strong, Emph = parsers.Emph, + StrikeThrough = parsers.fail, + Superscript = parsers.fail, + Subscript = parsers.fail, InlineNote = parsers.fail, NoteRef = parsers.fail, Citations = parsers.fail, @@ -3807,10 +3817,12 @@ M.extensions.citations = function(citation_nbsps) } return { extend_writer = function(self) + local options = self.options + local escape_citation = util.escaper( escaped_citation_chars, self.escaped_minimal_strings) - if self.hybrid then + if options.hybrid then self.citation = self.escape_minimal else self.citation = escape_citation @@ -4154,10 +4166,12 @@ end M.extensions.fenced_code = function(blank_before_code_fence) return { extend_writer = function(self) + local options = self.options + function self.fencedCode(i, s) if not self.is_writing then return "" end s = string.gsub(s, '[\r\n%s]*$', '') - local name = util.cache(self.cacheDir, s, nil, nil, ".verbatim") + local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") return {"\\markdownRendererInputFencedCode{",name,"}{",i,"}"} end end, extend_reader = function(self) @@ -4237,6 +4251,8 @@ M.extensions.fenced_code = function(blank_before_code_fence) parsers.EndlineExceptions = parsers.EndlineExceptions + fencestart syntax.EndlineExceptions = parsers.EndlineExceptions + + self.add_special_character("~") end } end @@ -4631,6 +4647,228 @@ M.extensions.pipe_tables = function(table_captions) end } end +M.extensions.strike_through = function() + return { + extend_writer = function(self) + function self.strike_through(s) + return {"\\markdownRendererStrikeThrough{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local StrikeThrough = ( + parsers.between(parsers.Inline, parsers.doubletildes, + parsers.doubletildes) + ) / writer.strike_through + + syntax.StrikeThrough = StrikeThrough + + self.add_special_character("~") + end + } +end +M.extensions.superscripts = function() + return { + extend_writer = function(self) + function self.superscript(s) + return {"\\markdownRendererSuperscript{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local Superscript = ( + parsers.between(parsers.Str, parsers.circumflex, parsers.circumflex) + ) / writer.superscript + + syntax.Superscript = Superscript + + self.add_special_character("^") + end + } +end +M.extensions.subscripts = function() + return { + extend_writer = function(self) + function self.subscript(s) + return {"\\markdownRendererSubscript{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local Subscript = ( + parsers.between(parsers.Str, parsers.tilde, parsers.tilde) + ) / writer.subscript + + syntax.Subscript = Subscript + + self.add_special_character("~") + end + } +end +M.extensions.fancy_lists = function() + return { + 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 syntax = self.syntax + 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.HorizontalRule + * (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.HorizontalRule + * 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 + + syntax.OrderedList = FancyList + + end + } +end function M.new(options) options = options or {} setmetatable(options, { __index = function (_, key) @@ -4683,6 +4921,26 @@ function M.new(options) table.insert(extensions, pipe_tables_extension) end + if options.strikeThrough then + local strike_through_extension = M.extensions.strike_through() + table.insert(extensions, strike_through_extension) + end + + if options.subscripts then + local subscript_extension = M.extensions.subscripts() + table.insert(extensions, subscript_extension) + end + + if options.superscripts then + local superscript_extension = M.extensions.superscripts() + table.insert(extensions, superscript_extension) + end + + if options.fancyLists then + local fancy_lists_extension = M.extensions.fancy_lists() + table.insert(extensions, fancy_lists_extension) + end + local writer = M.writer.new(options) local reader = M.reader.new(writer, options, extensions) |