diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d9353c6 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +.PHONY: test-all test-internal + +SHELL=/bin/bash +return_statement_line_number := $(shell grep -nE '^return' text-crossrefs.lua | cut -d ':' -f 1) +line_before_return := $(shell echo $$(($(return_statement_line_number) - 1))) + +test-all: test-internal test-context test-latex test-opendocument + +test-internal: text-crossrefs.lua + -rm --interactive=never test/tmp.lua + sed -n 1,$(line_before_return)p text-crossrefs.lua > test/tmp.lua + cat test/test-functions.lua >> test/tmp.lua + chmod -w test/tmp.lua + pandoc -L test/tmp.lua <<< '' + @echo -e '==========================\nAll internal tests passed.\n==========================\n' + rm --interactive=never test/tmp.lua + +test-%: text-crossrefs.lua sample.md + TESTED_FORMAT=$* pandoc -t native -L text-crossrefs.lua sample.md > test/tmp-$*.native + diff test/tmp-$*.native test/sample-$*.native + @echo -e '\n===============\ntest passed.\n===============\n' + rm test/tmp-$*.native + diff --git a/README.md b/README.md index b4c2037..c406a8b 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,47 @@ currently supports the following target formats: * odt * opendocument -It doesn't permit to refer to references in other files: if you want to do this, use text-extrefs. +It does not permit to refer to references in other files: if you want to do this, use text-extrefs. -N.-B.: When opening for the first time a DOCX or ODT/Opendocument file produced by Pandoc with text-crossrefs, you probably will have to refresh the fields in order to get the correct values. In LibreOffice, press `F9`; in Word, a dialog box should appear when the file opens. +## Format-specific preliminary notices + +### DOCX and ODT/Opendocument + +When opening for the first time a file produced by Pandoc with text-crossrefs, you probably will have to refresh the fields in order to get the correct values. In LibreOffice, press `F9`; in Word, a dialog box should appear when the file opens. + +### TeX-based formats + +All references are wrapped in a macro named `\tcrfenum`. It has two optional arguments: the first one is the reference type, the second specifies whether the prefix (e.g. “p. ”) should be printed or not (can be set to `withprefix`, `noprefix`, `yes` or `no`). The default values for these arguments should match those of `tcrf-default-reftype` and `tcrf-default-prefixref` (resp. `page` and `yes`, i.e. `withprefix`). The mandatory argument of `\tcrfenum` is a group containing a list of groups. Each of them contain a reference (either a single reference or a range). Here are some valid invocations: + + * \tcrfenum\[note\]\[withprefix\]{{lblone}{lbltwo}{lblthree}} + * \tcrfenum\[page\]\[noprefix\]{{lblone}{lbltwo}{lblthree}} + * \tcrfenum\[noprefix\]{{lblone}{lbltwo}{lblthree}} (the first argument defaults to `page`) + * \tcrfenum{{lblone}{lbltwo}{lblthree}} (the second argument defaults to `withprefix`) + * \tcrfenum{{only-one}} (even if the enumeration is limited to one item, it must be inside its own group) + * \tcrfenum{{lblone to lbltwo}{lblthree}} (the first reference points to a range) + +It is up to you to define `\tcrfenum` in your preamble. If your target format is LaTeX, it should be possible to define it as a wrapper for the `\zcref` macro provided by [the zref-clever package](https://ctan.org/pkg/zref-clever). Alternatively, you can use [my implementation](TODO), which supports ConTeXt, LaTeX and other formats. Here are some hints about the implementation: + + * [The `\tcrfenum` macro is supposed to output the numbers along with the prefixes and delimiters (e.g. “p. ” and “–”)]{#prefixes-tex}; + * In ConTeXt, there is no way to retrieve the note number from a `\reference` or a `\pagereference` contained in the note as is customary in LaTeX. to work around this, footnotes are labelled automatically with the first identifier attached to a span in the note prefixed with `note:`. Contrary to the ConTeXt syntax, this label is placed _after_ the footnote content, which implies redefining the `\footnote` macro. If your template includes the `header-includes` metadata variable like in the default template, this redefinition will happen automatically. Otherwise, you can copy-paste the following code in your preamble: + +``` {=tex} +\catcode`\@=11 +\let\origfootnote\footnote +\def\footnote#1#2{ + \def\tcrf@secondArg{#2}% + \ifx\tcrf@secondArg\tcrf@bracket + \def\tcrf@todo{\tcrf@footnote@withlabel{#1}#2} % + \else + \def\tcrf@todo{\origfootnote{#1}#2}% + \fi + \tcrf@todo +} +\def\tcrf@bracket{[} +\def\tcrf@footnote@withlabel#1[#2]{\origfootnote[#2]{#1}} +\catcode`\@=13 + +``` ## Usage @@ -34,92 +72,152 @@ identifier composed of alphanumeric characters, periods, colons, underscores and You can refer to it using another span with class `ref` containing the target's identifier. If the targetted span is part of a footnote, you can refer to it either by page or by note number according to -the value of the `type` attribute (defaults to `page`). For instance, this: +the value of the `reftype` attribute (defaults to `page`). For instance, this: ``` markdown -See [publication]{.ref} for the publication date. I gave my -opinion in [my-evaluation]{.ref type=note}, [my-evaluation]{.ref}. +See [publication]{.tcrf} for the publication date. I gave my +opinion in [my-evaluation]{.tcrf reftype=note}, [my-evaluation]{.tcrf}. ``` -will render in LaTeX output: +will render in ConTeXt or LaTeX output: ``` tex -See p. \pageref{publication} for the publication date. I expressed -my thoughts about it in \ref{my-evaluation}, -p. \pageref{my-evaluation}. +See \tcrfenum{{publication}} for the publication date. I expressed +my thoughts about it in \tcrfenum[note]{{my-evaluation}}, +\tcrfenum{{my-evaluation}}. ``` If you want to give a reference by note and page number like in the example above, you can also use the following shorthand: ```md -[my-evaluation]{.ref type=pagenote} +[my-evaluation]{.tcrf reftype=pagenote} ``` You can refer to headers as well using either explicit or automatically generated identifiers (see Pandoc user’s guide). +To suppress the prefixes (e.g. “p. ”), you can set the `prefixref` attribute to `no` (defaults to `yes`). It can be useful, for instance, for small manually formatted indexes[^1]: + +``` markdown +Gaboriau: [publication, my-evaluation, reception]{.tcrf prefixref=no} +``` + +[^1]: About the comma-delimited syntax used in this example, see [the section on enumerations below](#enums). + ### Page ranges You can refer to a page range like this: ``` markdown -If you want to know more about _L'Affaire Lerouge_, see [publication>reception]{.ref}. +If you want to know more about _L'Affaire Lerouge_, see [publication>reception]{.tcrf}. ``` The separator (here `>`) can be set to any string composed of characters other than alphanumeric, period, colon, underscore, hyphen and space. -In LaTeX and ConTeXt output, the page range will be printed as a simple page reference if the page numbers are identical. You can provide your own definition of the macro `\tcrfpagerangeref{}{}` in the preamble. In DOCX and ODT/Opendocument output, the same result can be achieved in a word processor by the means of automatic search and replace with regular expressions. +In LaTeX and ConTeXt output, the above-mentionned `\tcrfenum` macro should be defined so that the range is printed as a simple page reference if the page numbers are identical. The syntax of a range is: -## Enumerations +``` tex +\tcrfenum{{publication to reception}} +``` -Rather +In DOCX and ODT/Opendocument output, the same result can be achieved in a word processor by automatically searching and replacing duplicates with regular expressions and/or macros. + +### Enumerations {#enums} + +You can enumerate several references as a comma-delimited list, for instance: + +``` markdown +[ref-one, ref-two>ref-three, ref-four]{.tcrf} +``` + +In DOCX and ODT/Opendocument output, all these references will be printed, potentially resulting in unnecessary repetitions. +In TeX-based output formats, they will be wrapped in `\tcrfenum` like this: + +``` tex +\tcrfenum{{ref-one}{ref-two to ref-three}{ref-four}} +``` ## Customization +### Common options + The following metadata fields can be set as strings: + * `tcrf-references-enum-separator`: + * the string between two references in an enumeration in a reference span; can be composed of any characters not authorized in an identifier; + * defaults to `, ` (with a space after the comma). + * `tcrf-references-range-separator`: + * the string used to separate two references in a reference span; can be composed of any characters not authorized in an identifier; + * defaults to `>`. + * `tcrf-only-explicit-labels`: + * set it to `true` if you want that _tcrf_ handle only spans with class `label`; + * defaults to `false`. + * `tcrf-default-prefixref`: + * default value for the `prefixref` attribute; + * defaults to `yes`. + * `tcrf-default-reftype`: + * default value for the `reftype` attribute; + * defaults to `page`. + * `tcrf-filelabel-ref-separator`: + * only useful in conjunction with the text-exrefs filter; + * separator between external files' labels and references; + * defaults to `::`. + +### Options specific to DOCX and ODT/Opendocument + +Here are some metadata fields only useful in conjunction with `docx`, `odt` and `opendocument` formats (see [above](#prefixes-tex) why they are ignored with `context` and `latex`): + * `tcrf-page-prefix`: * “page” prefix; * defaults to `p. `. * `tcrf-pages-prefix`: * “pages” prefix; - * defaults to `p. `. + * defaults to `pp. `. * `tcrf-note-prefix`: * “note” prefix; * defaults to `n. `. + * `tcrf-notes-prefix`: + * “notes” prefix; + * defaults to `nn. `. * `tcrf-pagenote-separator`: - * the separator between the references when `type` is set to `pagenote`; + * the separator between the references when `reftype` is set to `pagenote`; * defaults to `, `. * `tcrf-pagenote-at-end`: * the string printed at the end of a pagenote reference; - * defaults to an empty string, can be used to achieve something like *n. 3 (p. 5)*. - * `tcrf-pagenote-order`: - * the order in which the references to note and page are printed; - * defaults to `pagefirst`, can be set to `notefirst`. - * `tcrf-references-range-separator`: - * the string used to separate two references in a reference span; can be composed of any character not authorized in an identifier other than space or tab; - * defaults to `>`. + * defaults to the empty string, can be used to achieve something like *n. 3 (p. 5)*. + * `tcrf-pagenote-factorize-first-prefix-in-enum`: + * defines if the prefixes of the type printed first in a reference to page and note should be repeated (e.g. “p. 6, n. 1 and p. 9, n. 3”) or expressed globally at the beginning of the enumeration (e.g. “pp. 6, n. 1 and 9, n. 3”); + * defaults to `no`, can be set to `yes`. + * `tcrf-pagenote-first-type`: + * the information that is printed first in references to page and note; + * defaults to `page`, can be set to `note`. * `tcrf-range-separator`: - * the string inserted between to page numbers in a range; - * defaults to `-`. + * the string inserted between the page numbers in a range; + * defaults to `–`. * `tcrf-references-enum-separator`: - * the string used to separate the elements of an enumeration in a reference span; can be composed of any character not authorized in an identifier other than space or tab; - * defaults to `;`. - * `tcrf-enum-separator`: + * the string used to separate the elements of an enumeration in a reference span; + * defaults to a comma followed by a space. + * `tcrf-multiple-delimiter`: * the string inserted between two elements (but the two last ones) in an enumeration; * defaults to a comma followed by a space. - * `tcrf-before-last-in-enum`: + * `tcrf-multiple-before-last`: * the string inserted between the two last elements in an enumeration; * defaults to `and` surrounded with spaces. - * `tcrf-only-explicit-labels`: - * set it to `true` if you want that _tcrf_ handle only spans with class `label`; - * defaults to `false`. - * `tcrf-default-info-type`: - * default value for the `type` attribute (`note`, `page` or `pagenote`); - * defaults to `page`. - * `tcrf-filelabel-ref-separator`: - * only useful in conjunction with the text-exrefs filter; - * separator between external files' labels and references; - * defaults to `::`. + +### Options specific to the formats based on TeX + +Since TeX is extensible, you may wish to support types other than `page`, `note` and `pagenote` for ConTeXt and LaTeX output. `tcrf-additional-types` can be provided with a list of supplementary accepted types, e.g.: + +``` yaml +tcrf-additional-types: +- line +- figure +``` + +In addition, the following metadata field can be used to control the rendering of ranges of labels in `\tcrfenum`: + + * `tcrf-range-delim-tcrfenum`: + * the delimiter between the labels of a range in the list of references passed to `\tcrfenum`; + * defaults to ` to ` (mind the spaces). ## Compatibility with other filters @@ -147,6 +245,6 @@ not that: [@Jones1973, p. 5-70[]{#ref-to-jones}; @Doe2004] ``` -You can set classes and attributes to your spans other than those defined by text-crossrefs (for instance `[some text]{#to-be-referred-to .highlighted color=red}` or `[reference]{.ref color=red}`). No span is removed. +You can set classes and attributes to your spans other than those defined by text-crossrefs (for instance `[some text]{#to-be-referred-to .highlighted color=red}` or `[reference]{.tcrf color=red}`). No span is removed. Text-crossrefs is fully compatible with text-extrefs. Whenever possible, when a metadata is not set for text-extrefs, its value is taken from its text-crossrefs equivalent, so that you don't need to duplicate similar variables. diff --git a/sample.md b/sample.md index 13c7f35..044150b 100644 --- a/sample.md +++ b/sample.md @@ -1,8 +1,12 @@ --- tcrf-pagenote-separator: '\ (' tcrf-pagenote-at-end: ')' +header-includes: | + \input{tex-aux/tcrfenum} --- +(About the notes, see [toc-notes-begin>toc-notes-end]{.tcrf}.) + Émile Gaboriau published [_L'Affaire Lerouge_ in 1866]{#publication}.[^1] @@ -10,18 +14,22 @@ tcrf-pagenote-at-end: ')' [It was very popular.]{#reception} -See [publication]{.ref} for the publication date. I expressed -my thoughts about it in [my-evaluation]{.ref type=pagenote}. +See [publication]{.tcrf} for the publication date. I expressed +my thoughts about it in [my-evaluation]{.tcrf reftype=pagenote}. -If you want to know more about _L'Affaire Lerouge_, see [publication>reception]{.ref}. +If you want to know more about _L'Affaire Lerouge_, see [publication>reception]{.tcrf}. Here are some precisions.[^2] [^2]: [Whatever format]{#format} you choose, you can [refer to a note]{#refer-to-note} by the identifier of [any of its spans. You can even [nest spans]{#nested-spans}!]{#which-identifier} -I want to refer to a note: +[I want to refer to a note]{#toc-notes-begin}: - * How can I refer to a note by its number? → See [refer-to-note]{.ref}. - * What formats are supported? → See [format]{.ref}. - * What if the note contains multiple spans with identifiers? → See [which-identifier]{.ref}. - * What happens if a span in contained in a span? → See [nested-spans]{.ref}. + * How can I refer to a note by its number? → See [refer-to-note]{.tcrf}. + * What formats are supported? → See [format]{.tcrf}. + * What if the note contains multiple spans with identifiers? → See [which-identifier]{.tcrf}. + * What happens if a span in contained in a span? → See [nested-spans]{.tcrf}. + * What are the notes? → [my-evaluation, format, refer-to-note]{.tcrf reftype=note} + * Where are the notes? → [my-evaluation, format]{.tcrf} + +[]{#toc-notes-end} diff --git a/test.md b/test.md deleted file mode 100644 index 8947f69..0000000 --- a/test.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -tcrf-pagenote-separator: '\ (' -tcrf-pagenote-at-end: ')' ---- - -Émile Gaboriau published [_L'Affaire Lerouge_ in -1866]{#publication}.[^1] - -[^1]: It is a very [fine piece of literature]{#my-evaluation}. - -[It was very popular.]{#reception} - -See [publication]{.ref} for the publication date. I expressed -my thoughts about it [my-evaluation]{.ref type=pagenote}. - -Page range: [publication>reception]{.ref} diff --git a/test/sample-context.native b/test/sample-context.native new file mode 100644 index 0000000..68cb961 --- /dev/null +++ b/test/sample-context.native @@ -0,0 +1,386 @@ +[ Para + [ Str "(About" + , Space + , Str "the" + , Space + , Str "notes," + , Space + , Str "see" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "context") + "\\tcrfenum{{toc-notes-begin to toc-notes-end}}" + ] + , Str ".)" + ] +, Para + [ Str "\201mile" + , Space + , Str "Gaboriau" + , Space + , Str "published" + , Space + , Str "" + , Span + ( "publication" , [] , [] ) + [ Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ] + , Space + , Str "in" + , SoftBreak + , Str "1866" + ] + , Str "." + , RawInline (Format "context") "" + , Note + [ Para + [ Str "It" + , Space + , Str "is" + , Space + , Str "a" + , Space + , Str "very" + , Space + , Str "" + , Span + ( "my-evaluation" , [] , [] ) + [ Str "fine" + , Space + , Str "piece" + , Space + , Str "of" + , Space + , Str "literature" + ] + , Str "." + ] + ] + , RawInline (Format "context") "[note:my-evaluation]" + ] +, Para + [ Str "" + , Span + ( "reception" , [] , [] ) + [ Str "It" + , Space + , Str "was" + , Space + , Str "very" + , Space + , Str "popular." + ] + ] +, Para + [ Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline (Format "context") "\\tcrfenum{{publication}}" ] + , Space + , Str "for" + , Space + , Str "the" + , Space + , Str "publication" + , Space + , Str "date." + , Space + , Str "I" + , Space + , Str "expressed" + , SoftBreak + , Str "my" + , Space + , Str "thoughts" + , Space + , Str "about" + , Space + , Str "it" + , Space + , Str "in" + , Space + , Span + ( "" , [ "tcrf" ] , [ ( "reftype" , "pagenote" ) ] ) + [ RawInline + (Format "context") "\\tcrfenum[pagenote]{{my-evaluation}}" + ] + , Str "." + ] +, Para + [ Str "If" + , Space + , Str "you" + , Space + , Str "want" + , Space + , Str "to" + , Space + , Str "know" + , Space + , Str "more" + , Space + , Str "about" + , Space + , Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ] + , Str "," + , Space + , Str "see" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "context") "\\tcrfenum{{publication to reception}}" + ] + , Str "." + ] +, Para + [ Str "Here" + , Space + , Str "are" + , Space + , Str "some" + , Space + , Str "precisions." + , RawInline (Format "context") "" + , Note + [ Para + [ Str "" + , Span + ( "format" , [] , [] ) + [ Str "Whatever" , Space , Str "format" ] + , Space + , Str "you" + , Space + , Str "choose," + , Space + , Str "you" + , Space + , Str "can" + , Space + , Str "" + , Span + ( "refer-to-note" , [] , [] ) + [ Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + ] + , Space + , Str "by" + , Space + , Str "the" + , Space + , Str "identifier" + , Space + , Str "of" + , Space + , Str "" + , Span + ( "which-identifier" , [] , [] ) + [ Str "any" + , Space + , Str "of" + , Space + , Str "its" + , Space + , Str "spans." + , Space + , Str "You" + , Space + , Str "can" + , Space + , Str "even" + , Space + , Str "" + , Span + ( "nested-spans" , [] , [] ) + [ Str "nest" , Space , Str "spans" ] + , Str "!" + ] + ] + ] + , RawInline (Format "context") "[note:format]" + ] +, Para + [ Str "" + , Span + ( "toc-notes-begin" , [] , [] ) + [ Str "I" + , Space + , Str "want" + , Space + , Str "to" + , Space + , Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + ] + , Str ":" + ] +, BulletList + [ [ Plain + [ Str "How" + , Space + , Str "can" + , Space + , Str "I" + , Space + , Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + , Space + , Str "by" + , Space + , Str "its" + , Space + , Str "number?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "context") "\\tcrfenum{{refer-to-note}}" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "formats" + , Space + , Str "are" + , Space + , Str "supported?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline (Format "context") "\\tcrfenum{{format}}" ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "if" + , Space + , Str "the" + , Space + , Str "note" + , Space + , Str "contains" + , Space + , Str "multiple" + , Space + , Str "spans" + , Space + , Str "with" + , Space + , Str "identifiers?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "context") "\\tcrfenum{{which-identifier}}" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "happens" + , Space + , Str "if" + , Space + , Str "a" + , Space + , Str "span" + , Space + , Str "in" + , Space + , Str "contained" + , Space + , Str "in" + , Space + , Str "a" + , Space + , Str "span?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "context") "\\tcrfenum{{nested-spans}}" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "are" + , Space + , Str "the" + , Space + , Str "notes?" + , Space + , Str "\8594" + , Space + , Span + ( "" , [ "tcrf" ] , [ ( "reftype" , "note" ) ] ) + [ RawInline + (Format "context") + "\\tcrfenum[note]{{my-evaluation}{format}{refer-to-note}}" + ] + ] + ] + , [ Plain + [ Str "Where" + , Space + , Str "are" + , Space + , Str "the" + , Space + , Str "notes?" + , Space + , Str "\8594" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "context") + "\\tcrfenum{{my-evaluation}{format}}" + ] + ] + ] + ] +, Para [ Str "" , Span ( "toc-notes-end" , [] , [] ) [] ] +] diff --git a/test/sample-latex.native b/test/sample-latex.native new file mode 100644 index 0000000..c061200 --- /dev/null +++ b/test/sample-latex.native @@ -0,0 +1,386 @@ +[ Para + [ Str "(About" + , Space + , Str "the" + , Space + , Str "notes," + , Space + , Str "see" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "latex") + "\\tcrfenum{{toc-notes-begin to toc-notes-end}}" + ] + , Str ".)" + ] +, Para + [ Str "\201mile" + , Space + , Str "Gaboriau" + , Space + , Str "published" + , Space + , RawInline (Format "latex") "\\label{publication}" + , Span + ( "publication" , [] , [] ) + [ Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ] + , Space + , Str "in" + , SoftBreak + , Str "1866" + ] + , Str "." + , RawInline (Format "latex") "" + , Note + [ Para + [ Str "It" + , Space + , Str "is" + , Space + , Str "a" + , Space + , Str "very" + , Space + , RawInline (Format "latex") "\\label{my-evaluation}" + , Span + ( "my-evaluation" , [] , [] ) + [ Str "fine" + , Space + , Str "piece" + , Space + , Str "of" + , Space + , Str "literature" + ] + , Str "." + ] + ] + , RawInline (Format "latex") "" + ] +, Para + [ RawInline (Format "latex") "\\label{reception}" + , Span + ( "reception" , [] , [] ) + [ Str "It" + , Space + , Str "was" + , Space + , Str "very" + , Space + , Str "popular." + ] + ] +, Para + [ Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline (Format "latex") "\\tcrfenum{{publication}}" ] + , Space + , Str "for" + , Space + , Str "the" + , Space + , Str "publication" + , Space + , Str "date." + , Space + , Str "I" + , Space + , Str "expressed" + , SoftBreak + , Str "my" + , Space + , Str "thoughts" + , Space + , Str "about" + , Space + , Str "it" + , Space + , Str "in" + , Space + , Span + ( "" , [ "tcrf" ] , [ ( "reftype" , "pagenote" ) ] ) + [ RawInline + (Format "latex") "\\tcrfenum[pagenote]{{my-evaluation}}" + ] + , Str "." + ] +, Para + [ Str "If" + , Space + , Str "you" + , Space + , Str "want" + , Space + , Str "to" + , Space + , Str "know" + , Space + , Str "more" + , Space + , Str "about" + , Space + , Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ] + , Str "," + , Space + , Str "see" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "latex") "\\tcrfenum{{publication to reception}}" + ] + , Str "." + ] +, Para + [ Str "Here" + , Space + , Str "are" + , Space + , Str "some" + , Space + , Str "precisions." + , RawInline (Format "latex") "" + , Note + [ Para + [ RawInline (Format "latex") "\\label{format}" + , Span + ( "format" , [] , [] ) + [ Str "Whatever" , Space , Str "format" ] + , Space + , Str "you" + , Space + , Str "choose," + , Space + , Str "you" + , Space + , Str "can" + , Space + , RawInline (Format "latex") "\\label{refer-to-note}" + , Span + ( "refer-to-note" , [] , [] ) + [ Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + ] + , Space + , Str "by" + , Space + , Str "the" + , Space + , Str "identifier" + , Space + , Str "of" + , Space + , RawInline (Format "latex") "\\label{which-identifier}" + , Span + ( "which-identifier" , [] , [] ) + [ Str "any" + , Space + , Str "of" + , Space + , Str "its" + , Space + , Str "spans." + , Space + , Str "You" + , Space + , Str "can" + , Space + , Str "even" + , Space + , RawInline (Format "latex") "\\label{nested-spans}" + , Span + ( "nested-spans" , [] , [] ) + [ Str "nest" , Space , Str "spans" ] + , Str "!" + ] + ] + ] + , RawInline (Format "latex") "" + ] +, Para + [ RawInline (Format "latex") "\\label{toc-notes-begin}" + , Span + ( "toc-notes-begin" , [] , [] ) + [ Str "I" + , Space + , Str "want" + , Space + , Str "to" + , Space + , Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + ] + , Str ":" + ] +, BulletList + [ [ Plain + [ Str "How" + , Space + , Str "can" + , Space + , Str "I" + , Space + , Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + , Space + , Str "by" + , Space + , Str "its" + , Space + , Str "number?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline (Format "latex") "\\tcrfenum{{refer-to-note}}" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "formats" + , Space + , Str "are" + , Space + , Str "supported?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline (Format "latex") "\\tcrfenum{{format}}" ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "if" + , Space + , Str "the" + , Space + , Str "note" + , Space + , Str "contains" + , Space + , Str "multiple" + , Space + , Str "spans" + , Space + , Str "with" + , Space + , Str "identifiers?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "latex") "\\tcrfenum{{which-identifier}}" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "happens" + , Space + , Str "if" + , Space + , Str "a" + , Space + , Str "span" + , Space + , Str "in" + , Space + , Str "contained" + , Space + , Str "in" + , Space + , Str "a" + , Space + , Str "span?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline (Format "latex") "\\tcrfenum{{nested-spans}}" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "are" + , Space + , Str "the" + , Space + , Str "notes?" + , Space + , Str "\8594" + , Space + , Span + ( "" , [ "tcrf" ] , [ ( "reftype" , "note" ) ] ) + [ RawInline + (Format "latex") + "\\tcrfenum[note]{{my-evaluation}{format}{refer-to-note}}" + ] + ] + ] + , [ Plain + [ Str "Where" + , Space + , Str "are" + , Space + , Str "the" + , Space + , Str "notes?" + , Space + , Str "\8594" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "latex") "\\tcrfenum{{my-evaluation}{format}}" + ] + ] + ] + ] +, Para + [ RawInline (Format "latex") "\\label{toc-notes-end}" + , Span ( "toc-notes-end" , [] , [] ) [] + ] +] diff --git a/test/sample-opendocument.native b/test/sample-opendocument.native new file mode 100644 index 0000000..d07ef34 --- /dev/null +++ b/test/sample-opendocument.native @@ -0,0 +1,397 @@ +[ Para + [ Str "(About" + , Space + , Str "the" + , Space + , Str "notes," + , Space + , Str "see" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "pp.\160000\8211000" + ] + , Str ".)" + ] +, Para + [ Str "\201mile" + , Space + , Str "Gaboriau" + , Space + , Str "published" + , Space + , Str "" + , Span + ( "publication" , [] , [] ) + [ Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ] + , Space + , Str "in" + , SoftBreak + , Str "1866" + ] + , Str "." + , RawInline (Format "opendocument") "" + , Note + [ Para + [ Str "It" + , Space + , Str "is" + , Space + , Str "a" + , Space + , Str "very" + , Space + , Str "" + , Span + ( "my-evaluation" , [] , [] ) + [ Str "fine" + , Space + , Str "piece" + , Space + , Str "of" + , Space + , Str "literature" + ] + , Str "." + ] + ] + , RawInline (Format "opendocument") "" + ] +, Para + [ Str "" + , Span + ( "reception" , [] , [] ) + [ Str "It" + , Space + , Str "was" + , Space + , Str "very" + , Space + , Str "popular." + ] + ] +, Para + [ Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "p.\160000" + ] + , Space + , Str "for" + , Space + , Str "the" + , Space + , Str "publication" + , Space + , Str "date." + , Space + , Str "I" + , Space + , Str "expressed" + , SoftBreak + , Str "my" + , Space + , Str "thoughts" + , Space + , Str "about" + , Space + , Str "it" + , Space + , Str "in" + , Space + , Span + ( "" , [ "tcrf" ] , [ ( "reftype" , "pagenote" ) ] ) + [ RawInline + (Format "opendocument") + "p.\160000\160(n.\160000)" + ] + , Str "." + ] +, Para + [ Str "If" + , Space + , Str "you" + , Space + , Str "want" + , Space + , Str "to" + , Space + , Str "know" + , Space + , Str "more" + , Space + , Str "about" + , Space + , Emph [ Str "L\8217Affaire" , Space , Str "Lerouge" ] + , Str "," + , Space + , Str "see" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "pp.\160000\8211000" + ] + , Str "." + ] +, Para + [ Str "Here" + , Space + , Str "are" + , Space + , Str "some" + , Space + , Str "precisions." + , RawInline (Format "opendocument") "" + , Note + [ Para + [ Str "" + , Span + ( "format" , [] , [] ) + [ Str "Whatever" , Space , Str "format" ] + , Space + , Str "you" + , Space + , Str "choose," + , Space + , Str "you" + , Space + , Str "can" + , Space + , Str "" + , Span + ( "refer-to-note" , [] , [] ) + [ Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + ] + , Space + , Str "by" + , Space + , Str "the" + , Space + , Str "identifier" + , Space + , Str "of" + , Space + , Str "" + , Span + ( "which-identifier" , [] , [] ) + [ Str "any" + , Space + , Str "of" + , Space + , Str "its" + , Space + , Str "spans." + , Space + , Str "You" + , Space + , Str "can" + , Space + , Str "even" + , Space + , Str "" + , Span + ( "nested-spans" , [] , [] ) + [ Str "nest" , Space , Str "spans" ] + , Str "!" + ] + ] + ] + , RawInline (Format "opendocument") "" + ] +, Para + [ Str "" + , Span + ( "toc-notes-begin" , [] , [] ) + [ Str "I" + , Space + , Str "want" + , Space + , Str "to" + , Space + , Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + ] + , Str ":" + ] +, BulletList + [ [ Plain + [ Str "How" + , Space + , Str "can" + , Space + , Str "I" + , Space + , Str "refer" + , Space + , Str "to" + , Space + , Str "a" + , Space + , Str "note" + , Space + , Str "by" + , Space + , Str "its" + , Space + , Str "number?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "p.\160000" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "formats" + , Space + , Str "are" + , Space + , Str "supported?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "p.\160000" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "if" + , Space + , Str "the" + , Space + , Str "note" + , Space + , Str "contains" + , Space + , Str "multiple" + , Space + , Str "spans" + , Space + , Str "with" + , Space + , Str "identifiers?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "p.\160000" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "happens" + , Space + , Str "if" + , Space + , Str "a" + , Space + , Str "span" + , Space + , Str "in" + , Space + , Str "contained" + , Space + , Str "in" + , Space + , Str "a" + , Space + , Str "span?" + , Space + , Str "\8594" + , Space + , Str "See" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "p.\160000" + ] + , Str "." + ] + ] + , [ Plain + [ Str "What" + , Space + , Str "are" + , Space + , Str "the" + , Space + , Str "notes?" + , Space + , Str "\8594" + , Space + , Span + ( "" , [ "tcrf" ] , [ ( "reftype" , "note" ) ] ) + [ RawInline + (Format "opendocument") + "nn.\160000, 000 and 000" + ] + ] + ] + , [ Plain + [ Str "Where" + , Space + , Str "are" + , Space + , Str "the" + , Space + , Str "notes?" + , Space + , Str "\8594" + , Space + , Span + ( "" , [ "tcrf" ] , [] ) + [ RawInline + (Format "opendocument") + "pp.\160000 and 000" + ] + ] + ] + ] +, Para [ Str "" , Span ( "toc-notes-end" , [] , [] ) [] ] +] diff --git a/test/test-functions.lua b/test/test-functions.lua new file mode 100644 index 0000000..d9d5c9e --- /dev/null +++ b/test/test-functions.lua @@ -0,0 +1,36 @@ +local refs + +refs = parse_references_enum('mylabel') +assert(#refs == 1) +assert(refs[1].anchor == 'mylabel') +assert(not refs[1].end_of_range) +assert(make_raw_content_tex(refs, 'page', true) + == '\\tcrfenum{{mylabel}}') +assert(make_raw_content_tex(refs, 'page', false) + == '\\tcrfenum[noprefix]{{mylabel}}') +assert(make_raw_content_tex(refs, 'note', true) + == '\\tcrfenum[note]{{mylabel}}') +assert(make_raw_content_tex(refs, 'note', false) + == '\\tcrfenum[note][noprefix]{{mylabel}}') + +refs = parse_references_enum('rangebeg>rangeend') +assert(#refs == 1) +assert(refs[1].anchor == 'rangebeg') +assert(refs[1].end_of_range == 'rangeend') +assert(make_raw_content_tex(refs, 'page', true) + == '\\tcrfenum{{rangebeg to rangeend}}') + +refs = parse_references_enum('first, second') +assert(#refs == 2) +assert(refs[1].anchor == 'first') +assert(refs[2].anchor == 'second') +assert(make_raw_content_tex(refs, 'page', true) + == '\\tcrfenum{{first}{second}}') + +refs = parse_references_enum('first, rangebeg>rangeend') +assert(#refs == 2) +assert(refs[1].anchor == 'first') +assert(refs[2].anchor == 'rangebeg') +assert(refs[2].end_of_range == 'rangeend') +assert(make_raw_content_tex(refs, 'page', true) + == '\\tcrfenum{{first}{rangebeg to rangeend}}') diff --git a/text-crossrefs.lua b/text-crossrefs.lua index 21b4030..d17a15d 100644 --- a/text-crossrefs.lua +++ b/text-crossrefs.lua @@ -1,14 +1,52 @@ --- TODO : permettre la citation de références multiples avec un seul préfixe --- [ref-one, ref-two>ref-four, ref-three]{.ref} → pp. 1, 3-6 et 12 --- Compléter README et test une fois que ce sera fait. +local stringify = pandoc.utils.stringify --- Begin of initialization - -local IDENTIFIER_PATTERN = '[%w_.:-]+' +local TEXT_CROSSREF_CLASS = 'tcrf' +local REF_TYPE_ATTR = 'reftype' +local PREFIXED_ATTR = 'prefixref' +local IS_CONFIG_ARRAY = { ['additional_types'] = true } local RAW_ATTRIBUTE -local IS_LABEL_SET_BY_PANDOC -local LABEL_TEMPLATE -local NOTELABEL_TEMPLATE + +-- ConTeXt-specific tweak in order to add the label to the footnote +--[[ + Placing the label in square brackets immediatly after \footnote + in the regular way would require unpacking the content + of the Note and wrapping them with the RawInlines + '\footnote[note:' .. label .. ']{' and '}'. + However, Notes have the strange property of being Inlines + that contain Blocks, so this would result in Blocks being + brought into the content of the object that contains the Note, + which would be invalid. + That's why we place the label at the end of the \footnote + and redefine the macro so that it takes it into account. +]]-- + +local function tweak_footnote_if_ConTeXt(metadata) + if RAW_ATTRIBUTE == 'context' then + local footnote_redefinition = [[ +\catcode`\@=11 +\let\origfootnote\footnote +\def\footnote#1#2{ + \def\tcrf@secondArg{#2}% + \def\tcrf@bracket{[}% + \ifx\tcrf@secondArg\tcrf@bracket + \def\tcrf@todo{\tcrf@footnote@withlabel{#1}#2} % + \else + \def\tcrf@todo{\origfootnote{#1}#2}% + \fi + \tcrf@todo +} +\def\tcrf@footnote@withlabel#1[#2]{\origfootnote[#2]{#1}} +\catcode`\@=13 + ]] + if not metadata['header-includes'] then + metadata['header-includes'] = pandoc.MetaBlocks(pandoc.RawBlock('context', '')) + end + metadata['header-includes']:insert(pandoc.RawBlock('context', footnote_redefinition)) + end + return metadata +end + +-- Configuration local function define_raw_attribute() if FORMAT == 'native' then @@ -39,34 +77,44 @@ local function define_label_template() end end -define_raw_attribute() -define_label_template() - --- End of initialization - --- Configuration - local config = { page_prefix = 'p. ', - pages_prefix = 'p. ', + pages_prefix = 'pp. ', note_prefix = 'n. ', - pagenote_order = 'pagefirst', + notes_prefix = 'nn. ', + pagenote_first_type = 'page', pagenote_separator = ', ', pagenote_at_end = '', + pagenote_factorize_first_prefix_in_enum = 'no', + multiple_delimiter = ', ', + multiple_before_last = ' and ', references_range_separator = '>', - range_separator = '-', + range_separator = '–', + references_enum_separator = ', ', only_explicit_labels = 'false', - default_info_type = 'page', - filelabel_ref_separator = '::' + default_reftype = 'page', + default_prefixref = 'yes', + filelabel_ref_separator = '::', + range_delim_tcrfenum = ' to ', + additional_types = {} +} + +local accepted_types = { + page = true, + note = true, + pagenote = true } local function format_config_to_openxml() - to_format = { 'page_prefix', + local to_format = { 'page_prefix', 'pages_prefix', 'note_prefix', + 'notes_prefix', 'pagenote_separator', 'pagenote_at_end', - 'range_separator' } + 'range_separator', + 'multiple_delimiter', + 'multiple_before_last' } for i = 1, #to_format do config[to_format[i]] = '' .. config[to_format[i]] .. '' @@ -76,81 +124,46 @@ end local function set_configuration_item_from_metadata(item, metamap) metakey = 'tcrf-' .. string.gsub(item, '_', '-') if metamap[metakey] then - -- The metadata values are Str in MetaInlines. - config[item] = metamap[metakey][1].c + if IS_CONFIG_ARRAY[item] then + -- The metadata values is a list of MetaInlines, + -- each of them contains a single Str. + for _, value_metalist in ipairs(metamap[metakey]) do + table.insert(config[item], value_metalist[1].text) + end + else + -- The metadata value is a single Str in a MetaInlines. + config[item] = metamap[metakey][1].text + end end end local function configure(metadata) + define_raw_attribute() + define_label_template() for item, _ in pairs(config) do set_configuration_item_from_metadata(item, metadata) end if RAW_ATTRIBUTE == 'openxml' then format_config_to_openxml() end + if RAW_ATTRIBUTE == 'context' or RAW_ATTRIBUTE == 'latex' then + for _, additional_type in ipairs(config.additional_types) do + accepted_types[additional_type] = true + end + end end -- End of configuration --- Extensions for the output document's format - -local function define_tex_macros(document) - if RAW_ATTRIBUTE == 'context' then - local footnote_redefinition = '\\let\\oldfootnote\\footnote\n' .. - '\\define[2]\\footnote{\\oldfootnote[#2]{#1}%\n' .. - '\\expandafter\\edef\\csname #2pagenumber\\endcsname{\\userpage}}\n' - local predefined_strings = - '\\define\\tcrfpage{' .. config.page_prefix .. '}\n' .. - '\\define\\tcrfpages{' .. config.pages_prefix .. '}\n' .. - '\\define\\tcrfrangesep{' .. config.range_separator .. '}\n' - local range_ref = '\\ifdefined\\tcrfpagerangeref\\else\n' .. - '\\define[2]\\tcrfpagerangeref{' .. - '\\if' .. - '\\csname #1pagenumber\\endcsname' .. - '\\csname #2pagenumber\\endcsname\n' .. - '\\tcrfpage\\at[#1]\n' .. - '\\else\n' .. - '\\tcrfpages\\at[#1]\\tcrfrangesep\\at[#2]\\fi}\n' .. - '\\fi\n' - local macros_block = pandoc.RawBlock('context', - footnote_redefinition .. - predefined_strings .. - range_ref) - table.insert(document.blocks, 1, macros_block) - elseif RAW_ATTRIBUTE == 'latex' then - local predefined_strings = - '\\newcommand*{\\tcrfpage}{' .. config.page_prefix .. '}\n' .. - '\\newcommand*{\\tcrfpages}{' .. config.pages_prefix .. '}\n' .. - '\\newcommand*{\\tcrfrangesep}{' .. config.range_separator .. '}\n' - local label_redefinition = '\\let\\oldlabel\\label\n' .. - '\\renewcommand*{\\label}[1]{\\oldlabel{#1}%\n' .. - '\\expandafter\\xdef\\csname #1pagenumber\\endcsname{\\thepage}}\n' - local range_ref = '\\ifdefined\\tcrfpagerangeref\\else\n' .. - '\\newcommand*{\\tcrfpagerangeref}[2]{%\n' .. - '\\if' .. - '\\csname #1pagenumber\\endcsname' .. - '\\csname #2pagenumber\\endcsname\n' .. - '\\tcrfpage\\pageref{#1}\n' .. - '\\else\n' .. - '\\tcrfpages\\pageref{#1}\\tcrfrangesep\\pageref{#2}\\fi}\n' .. - '\\fi\n' - local macros_block = pandoc.RawBlock('latex', - predefined_strings .. - label_redefinition .. - range_ref) - table.insert(document.blocks, 1, macros_block) - end - return document -end - --- End of the extensions for the output document's format - --- Identifiers +-- Preprocessing of identifiers on notes +-- Necessary for those output format where a note can be referred to +-- only via an identifier directly attached to it, not to its content local spans_to_note_labels = {} local current_odt_note_index = 0 local is_first_span_in_note = true local current_note_label +local text_to_note_labels = {} local function map_span_to_label(span) if RAW_ATTRIBUTE == 'opendocument' then @@ -168,7 +181,7 @@ local function map_spans_to_labels(container) for i = 1, #container.content do -- The tests must be separate in order to support spans inside spans. if container.content[i].t == 'Span' - and container.content[i].identifier ~= '' + and container.content[i].identifier then map_span_to_label(container.content[i]) end @@ -181,7 +194,8 @@ end local function map_spans_to_notelabels(note) if RAW_ATTRIBUTE == 'context' or RAW_ATTRIBUTE == 'opendocument' - or RAW_ATTRIBUTE == 'openxml' then + or RAW_ATTRIBUTE == 'openxml' + then is_first_span_in_note = true map_spans_to_labels(note) current_odt_note_index = current_odt_note_index + 1 @@ -189,12 +203,13 @@ local function map_spans_to_notelabels(note) end local function make_label(label) - if IS_LABEL_SET_BY_PANDOC then - return pandoc.Str('') - else - label_rawcode = string.gsub(LABEL_TEMPLATE, '{{label}}', label) - return pandoc.RawInline(RAW_ATTRIBUTE, label_rawcode) + -- pandoc.Null() cannot be used here because it is a Block element. + local label_pandoc_object = pandoc.Str('') + if not IS_LABEL_SET_BY_PANDOC then + local label_rawcode = string.gsub(LABEL_TEMPLATE, '{{label}}', label) + label_pandoc_object = pandoc.RawInline(RAW_ATTRIBUTE, label_rawcode) end + return label_pandoc_object end local function labelize_span(span) @@ -209,8 +224,8 @@ local current_note_labels = {} local collect_note_labels = { Span = function(span) - if span.identifier ~= '' and - (config.only_explicit_labels == 'false' or span.classes:includes('label')) + if span.identifier ~= '' + and (config.only_explicit_labels == 'false' or span.classes:includes('label')) then table.insert(current_note_labels, span.identifier) end @@ -218,227 +233,373 @@ local collect_note_labels = { } local function make_notelabel(pos) + -- About the strategy followed with ConTeXt, + -- see above tweak_footnote_if_ConTeXt. local raw_code = '' if pos == 'begin' then if RAW_ATTRIBUTE == 'openxml' then raw_code = string.gsub( '', - '{{label}}', current_note_labels[#current_note_labels]) + '{{label}}', current_note_labels[1]) end elseif pos == 'end' then - if RAW_ATTRIBUTE == 'context' then - local label = current_note_labels[1] .. '_note' - raw_code = '{' .. label .. '}' - elseif RAW_ATTRIBUTE == 'openxml' then + if RAW_ATTRIBUTE == 'openxml' then raw_code = string.gsub('', '{{label}}', current_note_labels[1]) + elseif RAW_ATTRIBUTE == 'context' then + raw_code = '[note:' .. current_note_labels[1] .. ']' end end return pandoc.RawInline(RAW_ATTRIBUTE, raw_code) end local function labelize_note(note) + local labelized_note local label_begin = make_notelabel('begin') local label_end = make_notelabel('end') return { label_begin, note, label_end } end +local function map_text_to_note_labels(current_note_labels) + local note_label = 'note:' .. current_note_labels[1] + for _, text_label in ipairs(current_note_labels) do + text_to_note_labels[text_label] = note_label + end +end + function set_notelabels(note) current_note_labels = {} pandoc.walk_inline(note, collect_note_labels) if #current_note_labels > 0 then + map_text_to_note_labels(current_note_labels) return labelize_note(note) end end --- End of identifiers-related code +-- End of preprocessing of identifiers on notes --- References +-- Gathering of data from the references span -local function is_reference_valid(ref) - if string.find(ref, '^[' .. IDENTIFIER_PATTERN .. ']') then - error('text-crossrefs.lua: Invalid character in reference: ' .. ref .. - '\nIdentifier and reference names can only contain' .. - ' alphanumerical characters, periods, underscores and hyphens.\n') - else - return true - end +local function new_ref(anchor, end_of_range) + -- A ref is a string-indexed table containing an "anchor" field + -- and an optionnal "end_of_range" field. + -- When "end_of_range" is non-nil, the ref is a range. + local ref = {} + ref.anchor = anchor + ref.end_of_range = end_of_range + return ref end -local function is_ref_external(rawref) - if string.find(rawref, config.filelabel_ref_separator, 1, true) then +local function is_ref_external(raw_references) + if string.find(raw_references, config.filelabel_ref_separator, 1, true) then return true else return false end end -local function is_ref_range(rawref) - if string.find(rawref, config.references_range_separator, 1, true) then - return true - else - return false +local function parse_possible_range(reference) + -- If reference is a string representing a range, + -- returns the strings representing the boundaries of the range. + -- Else, returns the string. + local range_first, range_second = nil + local delim_beg, delim_end = string.find(reference, + config.references_range_separator, + 1, true) + if delim_beg then + range_first = string.sub(reference, 1, delim_beg - 1) + range_second = string.sub(reference, delim_end + 1) end + return (range_first or reference), range_second end -function get_first_reference_end_index(range_separator_index) - if range_separator_index then - return range_separator_index - 1 - end -end - -local function get_first_reference(rawref) - local _, file_ref_separator_index = - string.find(rawref, config.filelabel_ref_separator, 1, true) - local range_separator_index, _ = - string.find(rawref, config.references_range_separator, 1, true) - local ref = string.sub(rawref, - (file_ref_separator_index or 0) + 1, - get_first_reference_end_index(range_separator_index)) - if is_reference_valid(ref) then return ref end -end - -local function get_second_reference(rawref) - local second_ref_begin_index - local _, file_ref_separator_index = - string.find(rawref, config.filelabel_ref_separator, 1, true) - if file_ref_separator_index then - _, file_ref_separator_index = - string.find(rawref, - config.filelabel_ref_separator, - config.file_ref_separator_index + 1, - true) - second_ref_begin_index = file_ref_separator_index + 1 - else - local _, range_separator_index, _ = - string.find(rawref, config.references_range_separator, 1, true) - second_ref_begin_index = range_separator_index + 1 - end - local ref = string.sub(rawref, second_ref_begin_index) - if is_reference_valid(ref) then return ref end -end - -local function analyze_reference_span(reference_span) - if #reference_span.content == 1 and reference_span.content[1].t == 'Str' then - raw_reference = reference_span.content[1].c - analyzed_reference = {} - analyzed_reference.is_external = is_ref_external(raw_reference) - analyzed_reference.is_range = is_ref_range(raw_reference) - if analyzed_reference.is_external then - analyzed_reference.filelabel = get_extfilelabel(raw_reference) +local function parse_next_reference(raw_references, beg_of_search) + -- Returns the ref corresponding to the next reference string + -- and the index which the parsing should be resumed at. + local current_ref = false + local next_ref_beg = nil + if beg_of_search < #raw_references then + -- The delimiter can be composed of more than one character. + local delim_beg, delim_end = string.find(raw_references, + config.references_enum_separator, + beg_of_search, true) + if delim_beg then + reference = string.sub(raw_references, beg_of_search, delim_beg - 1) + next_ref_beg = delim_end + 1 + else + reference = string.sub(raw_references, beg_of_search) + next_ref_beg = #raw_references end - analyzed_reference.first = get_first_reference(raw_reference) - if analyzed_reference.is_range then - analyzed_reference.second = get_second_reference(raw_reference) - end - return analyzed_reference - else - error('The content of a span with class ref must be a plain string.') + current_ref = new_ref(parse_possible_range(reference)) end + return current_ref, next_ref_beg end -local function insert_page_target_in_xml(target) +local function parse_references_enum(raw_references) + -- raw_refs is a string consisting of a list of single references or ranges. + -- Returns an array of refs produced by "new_ref" above. + local parsed_refs = {} + local current_ref, next_ref_beg = parse_next_reference(raw_references, 1) + while current_ref do + table.insert(parsed_refs, current_ref) + current_ref, next_ref_beg = + parse_next_reference(raw_references, next_ref_beg) + end + return parsed_refs +end + +local function error_on_attr(attr_key, attr_value, span_content) + error('Invalid value "' .. attr_value .. '" for attribute "' .. attr_key .. + '" in the span with class "' .. TEXT_CROSSREF_CLASS .. + '" whose content is "' .. stringify(span_content) .. '".') +end + +local function get_ref_type(span) + local ref_type = span.attributes[REF_TYPE_ATTR] or config.default_reftype + if not accepted_types[ref_type] then + error_on_attr(REF_TYPE_ATTR, ref_type, span.content) + end + return ref_type +end + +local function if_prefixed(span) + local prefixed_attr_value = span.attributes[PREFIXED_ATTR] or config.default_prefixref + if prefixed_attr_value ~= 'yes' and prefixed_attr_value ~= 'no' then + error_on_attr(PREFIXED_ATTR, prefixed_attr_value, span.content) + end + local is_prefixed = true + if prefixed_attr_value == 'no' then is_prefixed = false end + return is_prefixed +end + +-- End of gathering of data from the references span + +-- Formatting references as raw inlines. + +local function make_tcrfenum_first_arg(ref_type) + local ref_type_is_explicit = ref_type ~= config.default_reftype + local tcrfenum_first_arg = '' + if ref_type_is_explicit then + tcrfenum_first_arg = '[' .. ref_type .. ']' + end + return tcrfenum_first_arg +end + +local function make_tcrfenum_second_arg(is_prefixed) + local is_prefixed_is_explicit = is_prefixed ~= (config.default_prefixref == 'yes') + local tcrfenum_second_arg = '' + local is_prefixed_string = '' + if is_prefixed_is_explicit then + if is_prefixed then + is_prefixed_string = 'withprefix' + else + is_prefixed_string = 'noprefix' + end + tcrfenum_second_arg = '[' .. is_prefixed_string .. ']' + end + return tcrfenum_second_arg +end + +local function make_tcrfenum_references_list(refs, ref_type) + local tcrfenum_references_list = '' + for i = 1, #refs do + local ref = refs[i] + local anchor = ref.anchor + if FORMAT == 'context' + and (ref_type == 'note' or ref_type == 'pagenote') + then + anchor = text_to_note_labels[anchor] + end + local texified_ref = '{' .. anchor + if ref.end_of_range then + texified_ref = texified_ref .. config.range_delim_tcrfenum .. ref.end_of_range + end + texified_ref = texified_ref .. '}' + tcrfenum_references_list = tcrfenum_references_list .. texified_ref + end + return tcrfenum_references_list +end + +local function make_raw_content_tex(refs, ref_type, is_prefixed) + local texified_references = '' + texified_references = '\\tcrfenum' + .. make_tcrfenum_first_arg(ref_type) + .. make_tcrfenum_second_arg(is_prefixed) + .. '{' .. make_tcrfenum_references_list(refs, ref_type) .. '}' + return texified_references +end + +local function make_prefix_xml(ref_type, is_plural) + local prefix = '' + if is_plural then + prefix = config[ref_type .. 's_prefix'] + else + prefix = config[ref_type .. '_prefix'] + end + return prefix +end + +local function make_page_reference_xml(target, is_prefixed) + local xml_page_ref = '' + if is_prefixed then + xml_page_ref = make_prefix_xml('page', false) + end if RAW_ATTRIBUTE == 'opendocument' then - return '000' elseif RAW_ATTRIBUTE == 'openxml' then - return '' .. + xml_page_ref = xml_page_ref .. + '' .. ' PAGEREF ' .. target .. ' \\h ' .. '' .. '000' .. '' end + return xml_page_ref end -local function format_page_reference(target) - if RAW_ATTRIBUTE == 'context' then - return config.page_prefix .. '\\at[' .. target .. ']' - elseif RAW_ATTRIBUTE == 'latex' then - return config.page_prefix .. '\\pageref{' .. target .. '}' - elseif RAW_ATTRIBUTE == 'opendocument' then - return config.page_prefix .. insert_page_target_in_xml(target) - elseif RAW_ATTRIBUTE == 'openxml' then - return config.page_prefix .. insert_page_target_in_xml(target) +local function make_pagerange_reference_xml(first, second, is_prefixed) + local prefix = '' + if is_prefixed then prefix = make_prefix_xml('page', true) end + return prefix .. make_page_reference_xml(first, false) .. + config.range_separator .. make_page_reference_xml(second, false) +end + +local function make_note_reference_xml(target, is_prefixed) + local note_ref_xml = '' + if is_prefixed then + note_ref_xml = make_prefix_xml('note', false) end -end - -local function format_pagerange_reference(first, second) - if RAW_ATTRIBUTE == 'context' or RAW_ATTRIBUTE == 'latex' then - return '\\tcrfpagerangeref{' .. first .. '}{' .. second .. '}' - elseif RAW_ATTRIBUTE == 'opendocument' or RAW_ATTRIBUTE == 'openxml' then - return config.pages_prefix .. insert_page_target_in_xml(first) .. - config.range_separator .. insert_page_target_in_xml(second) - end -end - -local function format_note_reference(target) - if RAW_ATTRIBUTE == 'context' then - return config.note_prefix .. '\\in[' .. spans_to_note_labels[target] .. '_note' .. ']' - elseif RAW_ATTRIBUTE == 'latex' then - return config.note_prefix .. '\\ref{' .. target .. '}' - elseif RAW_ATTRIBUTE == 'opendocument' then - return config.note_prefix .. '000' + (spans_to_note_labels[target] or '') .. '">000' elseif RAW_ATTRIBUTE == 'openxml' then - return config.note_prefix .. + note_ref_xml = note_ref_xml .. '' .. ' NOTEREF ' .. - target .. '_Note' .. ' \\h ' .. + (spans_to_note_labels[target] or '') .. '_Note' .. ' \\h ' .. '' .. '000' .. '' end + return note_ref_xml end -local function format_pagenote_reference(target) - if config.pagenote_order == 'pagefirst' then - return format_page_reference(target) .. config.pagenote_separator .. - format_note_reference(target) .. config.pagenote_at_end - elseif config.pagenote_order == 'notefirst' then - return format_note_reference(target) .. config.pagenote_separator .. - format_page_reference(target) .. config.pagenote_at_end - else - error('tcrf-pagenote-order must be set either to pagefirst or notefirst.') +local function make_pagenote_reference_xml(target, is_prefixed) + local pagenote_ref_xml = '' + if is_prefixed then + pagenote_ref_xml = make_prefix_xml(config.pagenote_first_type, false) end -end - -local function format_reference(target, info_type) - if info_type == 'page' and target.is_range then - return format_pagerange_reference(target.first, target.second) - elseif info_type == 'page' then - return format_page_reference(target.first) - elseif info_type == 'note' then - return format_note_reference(target.first) - elseif info_type == 'pagenote' then - return format_pagenote_reference(target.first) + if config.pagenote_first_type == 'page' then + pagenote_ref_xml = pagenote_ref_xml .. + make_page_reference_xml(target, false) .. + config.pagenote_separator .. make_note_reference_xml(target, true) .. + config.pagenote_at_end + elseif config.pagenote_first_type == 'note' then + pagenote_ref_xml = pagenote_ref_xml .. + make_note_reference_xml(target, false) .. + config.pagenote_separator .. make_page_reference_xml(target, true) .. + config.pagenote_at_end else - error('Invalid value for attribute type in span with class ref: ' .. - info_type) + error('“tcrf-pagenote-first-type” must be set either to “page” or “note”.') end + return pagenote_ref_xml end -local function make_reference(span) - if span.classes:includes('ref') then - local target = analyze_reference_span(span) - if not target.is_external then - local info_type = span.attributes.type or config.default_info_type - local formatted_reference = format_reference(target, info_type) - span.content[1] = pandoc.RawInline(RAW_ATTRIBUTE, formatted_reference) - return span +local function make_reference_xml(ref, ref_type, is_prefixed) + local reference_xml = '' + if ref_type == 'page' and ref.end_of_range then + reference_xml = + make_pagerange_reference_xml(ref.anchor, ref.end_of_range, is_prefixed) + elseif ref_type == 'page' then + reference_xml = make_page_reference_xml(ref.anchor, is_prefixed) + elseif ref_type == 'note' then + reference_xml = make_note_reference_xml(ref.anchor, is_prefixed) + elseif ref_type == 'pagenote' then + reference_xml = make_pagenote_reference_xml(ref.anchor, is_prefixed) + end + return reference_xml +end + +local function make_global_prefix_xml(ref_type) + local global_prefix_xml = '' + local prefix_type = ref_type + if ref_type == 'pagenote' then + prefix_type = config.pagenote_first_type + end + global_prefix_xml = make_prefix_xml(prefix_type, true) + return global_prefix_xml +end + +local function make_references_xml(refs, ref_type, is_prefixed) + local references_xml = '' + for i = 1, #refs do + references_xml = references_xml .. + make_reference_xml(refs[i], ref_type, is_prefixed) + if i < #refs then + if i < #refs - 1 then + references_xml = references_xml .. config.multiple_delimiter + else + references_xml = references_xml .. config.multiple_before_last + end end end + return references_xml end --- End of references-related code +local function make_raw_content_xml(refs, ref_type, is_prefixed) + local is_enumeration = #refs > 1 + local global_prefix = '' + if is_enumeration and is_prefixed + and (ref_type ~= 'pagenote' + or pagenote_factorize_first_prefix_in_enum == 'yes') + then + global_prefix = make_global_prefix_xml(ref_type) + is_prefixed = false + end + local references_raw_xml = make_references_xml(refs, ref_type, is_prefixed) + return global_prefix .. references_raw_xml +end + +local function make_raw_content(refs, ref_type, is_prefixed) + local raw_content = '' + if RAW_ATTRIBUTE == 'context' or RAW_ATTRIBUTE == 'latex' then + raw_content = make_raw_content_tex(refs, ref_type, is_prefixed) + else + raw_content = make_raw_content_xml(refs, ref_type, is_prefixed) + end + return raw_content +end + +local function format_references(refs, ref_type, is_prefixed) + local raw_content = make_raw_content(refs, ref_type, is_prefixed) + return pandoc.RawInline(RAW_ATTRIBUTE, raw_content) +end + +local function format_enum(span) + -- A reference is a Str contained in a span representing a label or a range of labels. + -- A ref is a ref object produced by the function "new_ref" defined above. + if span.classes:includes(TEXT_CROSSREF_CLASS) + and not(is_ref_external(stringify(span.content))) + then + local refs = parse_references_enum(stringify(span.content)) + local ref_type = get_ref_type(span) + local is_prefixed = if_prefixed(span) + span.content = format_references(refs, ref_type, is_prefixed) + end + return span +end return { { Meta = configure }, - { Pandoc = define_tex_macros }, + { Meta = tweak_footnote_if_ConTeXt }, { Note = set_notelabels }, { Note = map_spans_to_notelabels }, { Span = labelize_span }, - { Span = make_reference } + { Span = format_enum } }