From 5f9c0f07808978bfa98aa0192926d25804043b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Novotn=C3=BD?= Date: Mon, 27 Jun 2022 20:54:56 +0200 Subject: [PATCH] Options `citations` and `citationNbsps` --- markdown.dtx | 345 +++++++++++++++++++++++++-------------------------- 1 file changed, 171 insertions(+), 174 deletions(-) diff --git a/markdown.dtx b/markdown.dtx index 80c70b342..3fb3be75b 100644 --- a/markdown.dtx +++ b/markdown.dtx @@ -3506,7 +3506,7 @@ defaultOptions.citationNbsps = true % : true - : Enable the pandoc citation syntax extension: + : Enable the Pandoc citation syntax extension: ``` md Here is a simple parenthetical citation [@doe99] and here @@ -3526,7 +3526,7 @@ defaultOptions.citationNbsps = true : false - : Disable the pandoc citation syntax extension. + : Disable the Pandoc citation syntax extension. % \end{markdown} % \iffalse @@ -18263,8 +18263,8 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define tables \luamdef{escaped_uri_chars}, \luamdef{escaped_citation_chars}, -% and \luamdef{escaped_minimal_strings} containing the mapping from special +% Define tables \luamdef{escaped_uri_chars} and +% \luamdef{escaped_minimal_strings} containing the mapping from special % plain characters and character strings that always need to be escaped. % % \end{markdown} @@ -18275,13 +18275,6 @@ function M.writer.new(options) ["%"] = "\\markdownRendererPercentSign{}", ["\\"] = "\\markdownRendererBackslash{}", } - local escaped_citation_chars = { - ["{"] = "\\markdownRendererLeftBrace{}", - ["}"] = "\\markdownRendererRightBrace{}", - ["%"] = "\\markdownRendererPercentSign{}", - ["\\"] = "\\markdownRendererBackslash{}", - ["#"] = "\\markdownRendererHash{}", - } local escaped_minimal_strings = { ["^^"] = "\\markdownRendererCircumflex\\markdownRendererCircumflex ", ["☒"] = "\\markdownRendererTickedBox{}", @@ -18316,15 +18309,12 @@ function M.writer.new(options) % \begin{markdown} % % Use the \luamref{escaped_chars}, \luamref{escaped_uri_chars}, -% \luamref{escaped_citation_chars}, and \luamref{escaped_minimal_strings} tables -% to create the \luamdef{escape}, \luamdef{escape_citation}, +% and \luamref{escaped_minimal_strings} tables to create the \luamdef{escape}, % \luamdef{escape_uri}, and \luamdef{escape_minimal} escaper functions. % % \end{markdown} % \begin{macrocode} local escape = util.escaper(escaped_chars, escaped_minimal_strings) - local escape_citation = util.escaper(escaped_citation_chars, - escaped_minimal_strings) local escape_uri = util.escaper(escaped_uri_chars, escaped_minimal_strings) local escape_minimal = util.escaper({}, escaped_minimal_strings) % \end{macrocode} @@ -18332,22 +18322,18 @@ function M.writer.new(options) % \begin{markdown} % % Define \luamdef{writer->string} as a function that will transform an input -% plain text span `s` to the output format, \luamdef{writer->citation} as a -% function that will transform an input citation name `c` to the output format, -% and \luamdef{writer->uri} as a function that will transform an input -% \acro{uri} `u` to the output format. If the \Opt{hybrid} option is enabled, -% use the \luamref{escape_minimal}. Otherwise, use the \luamref{escape}, -% \luamref{escape_citation}, and \luamref{escape_uri} functions. +% plain text span `s` to the output format and \luamdef{writer->uri} as a +% function that will transform an input \acro{uri} `u` to the output format. +% If the \Opt{hybrid} option is enabled, use the \luamref{escape_minimal}. +% Otherwise, use the \luamref{escape}, and \luamref{escape_uri} functions. % % \end{markdown} % \begin{macrocode} if options.hybrid then self.string = escape_minimal - self.citation = escape_minimal self.uri = escape_minimal else self.string = escape - self.citation = escape_citation self.uri = escape_uri end % \end{macrocode} @@ -19002,35 +18988,6 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define \luamdef{writer->citations} as a function that will transform an -% input array of citations `cites` to the output format. If `text_cites` -% is enabled, the citations should be rendered in-text, when applicable. -% The `cites` array contains tables with the following keys and values: -% \begin{itemize} -% \item`suppress_author` -- If the value of the key is true, then the -% author of the work should be omitted in the citation, when applicable. -% \item`prenote` -- The value of the key is either `nil` or a rope -% that should be inserted before the citation. -% \item`postnote` -- The value of the key is either `nil` or a rope -% that should be inserted after the citation. -% \item`name` -- The value of this key is the citation name. -% \end{itemize} -% -% \end{markdown} -% \begin{macrocode} - function self.citations(text_cites, cites) - local buffer = {"\\markdownRenderer", text_cites and "TextCite" or "Cite", - "{", #cites, "}"} - for _,cite in ipairs(cites) do - buffer[#buffer+1] = {cite.suppress_author and "-" or "+", "{", - cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"} - end - return buffer - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->get_state} as a function that returns the current % state of the writer, where the state of a writer are its mutable member % variables. @@ -19141,8 +19098,6 @@ parsers.letter = R("AZ","az") parsers.alphanumeric = R("AZ","az","09") parsers.keyword = parsers.letter * parsers.alphanumeric^0 -parsers.citation_chars = parsers.alphanumeric - + S("#$%&-+<>~/_") parsers.internal_punctuation = S(":;,.?") parsers.doubleasterisks = P("**") @@ -19547,65 +19502,6 @@ parsers.localfilepath % \par % \begin{markdown} % -%#### Parsers Used for Citations -% -% \end{markdown} -% \begin{macrocode} -parsers.citation_name = Cs(parsers.dash^-1) * parsers.at - * Cs(parsers.citation_chars - * (((parsers.citation_chars + parsers.internal_punctuation - - parsers.comma - parsers.semicolon) - * -#((parsers.internal_punctuation - parsers.comma - - parsers.semicolon)^0 - * -(parsers.citation_chars + parsers.internal_punctuation - - parsers.comma - parsers.semicolon)))^0 - * parsers.citation_chars)^-1) - -parsers.citation_body_prenote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + (parsers.anyescaped - - (parsers.rbracket + parsers.blankline^2)) - - (parsers.spnl * parsers.dash^-1 * parsers.at))^0) - -parsers.citation_body_postnote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + (parsers.anyescaped - - (parsers.rbracket + parsers.semicolon - + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) - -parsers.citation_body_chunk - = parsers.citation_body_prenote - * parsers.spnl * parsers.citation_name - * (parsers.internal_punctuation - parsers.semicolon)^-1 - * parsers.spnl * parsers.citation_body_postnote - -parsers.citation_body - = parsers.citation_body_chunk - * (parsers.semicolon * parsers.spnl - * parsers.citation_body_chunk)^0 - -parsers.citation_headless_body_postnote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + (parsers.anyescaped - - (parsers.rbracket + parsers.at - + parsers.semicolon + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) - -parsers.citation_headless_body - = parsers.citation_headless_body_postnote - * (parsers.sp * parsers.semicolon * parsers.spnl - * parsers.citation_body_chunk)^0 -% \end{macrocode} -% \par -% \begin{markdown} -% %#### Parsers Used for Footnotes % % \end{markdown} @@ -20129,37 +20025,6 @@ function M.reader.new(writer, options, extensions) % \par % \begin{markdown} % -%#### Parsers Used for Citations (local) -% -% \end{markdown} -% \begin{macrocode} - parsers.citations = function(text_cites, raw_cites) - local function normalize(str) - if str == "" then - str = nil - else - str = (options.citationNbsps and - self.parser_functions.parse_inlines_nbsp or - self.parser_functions.parse_inlines)(str) - end - return str - end - - local cites = {} - for i = 1,#raw_cites,4 do - cites[#cites+1] = { - prenote = normalize(raw_cites[i]), - suppress_author = raw_cites[i+1] == "-", - name = writer.citation(raw_cites[i+2]), - postnote = normalize(raw_cites[i+3]), - } - end - return writer.citations(text_cites, cites) - end -% \end{macrocode} -% \par -% \begin{markdown} -% %#### Parsers Used for Footnotes (local) % % \end{markdown} @@ -20419,28 +20284,6 @@ function M.reader.new(writer, options, extensions) parsers.Image = parsers.DirectImage + parsers.IndirectImage - parsers.TextCitations = Ct((parsers.spnl - * Cc("") - * parsers.citation_name - * ((parsers.spnl - * parsers.lbracket - * parsers.citation_headless_body - * parsers.rbracket) + Cc("")))^1) - / function(raw_cites) - return parsers.citations(true, raw_cites) - end - - parsers.ParenthesizedCitations - = Ct((parsers.spnl - * parsers.lbracket - * parsers.citation_body - * parsers.rbracket)^1) - / function(raw_cites) - return parsers.citations(false, raw_cites) - end - - parsers.Citations = parsers.TextCitations + parsers.ParenthesizedCitations - -- avoid parsing long strings of * or _ as emph/strong parsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4 / writer.string @@ -20825,7 +20668,7 @@ function M.reader.new(writer, options, extensions) Emph = parsers.Emph, InlineNote = parsers.InlineNote, NoteRef = parsers.NoteRef, - Citations = parsers.Citations, + Citations = parsers.fail, Link = parsers.Link, Image = parsers.Image, Code = parsers.Code, @@ -20853,10 +20696,6 @@ function M.reader.new(writer, options, extensions) extension.extend_reader(self) end - if not options.citations then - self.syntax.Citations = parsers.fail - end - if not options.contentBlocks then self.syntax.ContentBlock = parsers.fail end @@ -21028,12 +20867,165 @@ M.extensions = {} % \end{macrocode} % \begin{markdown} % +%#### Citations +% +% The \luamdef{extensions.citations} function implements the Pandoc citation +% syntax extension. When the `citation_nbsps` parameter is `true`, the syntax +% extension will replace regular spaces with non-breaking spaces inside the +% prenotes and postnotes of citations. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.citations = function(citation_nbsps) + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->citations} as a function that will transform an +% input array of citations `cites` to the output format. If `text_cites` +% is enabled, the citations should be rendered in-text, when applicable. +% The `cites` array contains tables with the following keys and values: +% \begin{itemize} +% \item`suppress_author` -- If the value of the key is true, then the +% author of the work should be omitted in the citation, when applicable. +% \item`prenote` -- The value of the key is either `nil` or a rope +% that should be inserted before the citation. +% \item`postnote` -- The value of the key is either `nil` or a rope +% that should be inserted after the citation. +% \item`name` -- The value of this key is the citation name. +% \end{itemize} +% +% \end{markdown} +% \begin{macrocode} + function self.citations(text_cites, cites) + local buffer = {"\\markdownRenderer", text_cites and "TextCite" or "Cite", + "{", #cites, "}"} + for _,cite in ipairs(cites) do + buffer[#buffer+1] = {cite.suppress_author and "-" or "+", "{", + cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"} + end + return buffer + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + citation_chars = parsers.alphanumeric + + S("#$%&-+<>~/_") + + citation_name = Cs(parsers.dash^-1) * parsers.at + * Cs(citation_chars + * (((citation_chars + parsers.internal_punctuation + - parsers.comma - parsers.semicolon) + * -#((parsers.internal_punctuation - parsers.comma + - parsers.semicolon)^0 + * -(citation_chars + parsers.internal_punctuation + - parsers.comma - parsers.semicolon)))^0 + * citation_chars)^-1) + + citation_body_prenote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + (parsers.anyescaped + - (parsers.rbracket + parsers.blankline^2)) + - (parsers.spnl * parsers.dash^-1 * parsers.at))^0) + + citation_body_postnote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + (parsers.anyescaped + - (parsers.rbracket + parsers.semicolon + + parsers.blankline^2)) + - (parsers.spnl * parsers.rbracket))^0) + + citation_body_chunk + = citation_body_prenote + * parsers.spnl * citation_name + * (parsers.internal_punctuation - parsers.semicolon)^-1 + * parsers.spnl * citation_body_postnote + + citation_body = citation_body_chunk + * (parsers.semicolon * parsers.spnl + * citation_body_chunk)^0 + + citation_headless_body_postnote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + (parsers.anyescaped + - (parsers.rbracket + parsers.at + + parsers.semicolon + parsers.blankline^2)) + - (parsers.spnl * parsers.rbracket))^0) + + citation_headless_body + = citation_headless_body_postnote + * (parsers.sp * parsers.semicolon * parsers.spnl + * citation_body_chunk)^0 + + citations = function(text_cites, raw_cites) + local function normalize(str) + if str == "" then + str = nil + else + str = (citation_nbsps and + self.parser_functions.parse_inlines_nbsp or + self.parser_functions.parse_inlines)(str) + end + return str + end + + local cites = {} + for i = 1,#raw_cites,4 do + cites[#cites+1] = { + prenote = normalize(raw_cites[i]), + suppress_author = raw_cites[i+1] == "-", + name = writer.citation(raw_cites[i+2]), + postnote = normalize(raw_cites[i+3]), + } + end + return writer.citations(text_cites, cites) + end + + TextCitations = Ct((parsers.spnl + * Cc("") + * citation_name + * ((parsers.spnl + * parsers.lbracket + * citation_headless_body + * parsers.rbracket) + Cc("")))^1) + / function(raw_cites) + return parsers.citations(true, raw_cites) + end + + ParenthesizedCitations + = Ct((parsers.spnl + * parsers.lbracket + * citation_body + * parsers.rbracket)^1) + / function(raw_cites) + return parsers.citations(false, raw_cites) + end + + Citations = TextCitations + ParenthesizedCitations + + syntax.Citations = Citations + end + } +end +% \end{macrocode} +% \begin{markdown} +% %#### Fenced Code % % The \luamdef{extensions.fenced_code} function implements the commonmark -% fenced code block extension. When the `blank_before_code_fence` parameter is -% `true`, the syntax extension requires between a paragraph and the following -% fenced code block. +% fenced code block syntax extension. When the `blank_before_code_fence` +% parameter is `true`, the syntax extension requires between a paragraph and +% the following fenced code block. % % \end{markdown} % \begin{macrocode} @@ -21319,6 +21311,11 @@ function M.new(options) % \begin{macrocode} extensions = {} + if options.citations then + citations_extension = M.extensions.citations(options.citationNbsps) + table.insert(extensions, citations_extension) + end + if options.fencedCode then fenced_code_extension = M.extensions.fenced_code(options.blankBeforeCodeFence) table.insert(extensions, fenced_code_extension)