% % Copyright (c) 2021-2024 Zeping Lee % Released under the MIT License. % Repository: https://github.com/zepinglee/citeproc-lua % % ## Citation commands \DeclareDocumentCommand \cite { s o o m } { \IfBooleanTF {#1} { \bool_if:NTF \l__csl_note_bool { \__csl_cite_in_text:nnnn {#2} {#3} {#4} { } } { \__csl_cite_in_text:nnnn {#2} {#3} {#4} { suppress-author } } } { \bool_if:NTF \l__csl_note_bool { \__csl_cite_note:nnnn {#2} {#3} {#4} { } } { \__csl_cite_in_text:nnnn {#2} {#3} {#4} { } } } } \NewDocumentCommand \parencite { s o o m } { \IfBooleanTF {#1} { \bool_if:NTF \l__csl_note_bool { \__csl_cite_in_text:nnnn {#2} {#3} {#4} { } } { \__csl_cite_in_text:nnnn {#2} {#3} {#4} { suppress-author } } } { \__csl_cite_parens:nnn {#2} {#3} {#4} } } \NewDocumentCommand \citep { o o m } { \__csl_cite_parens:nnn {#1} {#2} {#3} } \NewDocumentCommand \textcite { o o m } { \__csl_text_cite:nnn {#1} {#2} {#3} } \NewDocumentCommand \citet { o o m } { \__csl_text_cite:nnn {#1} {#2} {#3} } \NewDocumentCommand \footcite { o o m } { \__csl_cite_note:nnnn {#1} {#2} {#3} { } } % \cites[⟨prenote⟩][⟨postnote⟩]{⟨key⟩}...[⟨prenote⟩][⟨postnote⟩]{⟨key⟩} \NewDocumentCommand \cites { } { \__csl_cites: } \NewDocumentCommand \citeauthor { o o m } { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { author-only } } \NewDocumentCommand \citeyear{ o o m } { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { cite-year } } % Suppresses the author (from `natbib`). \NewDocumentCommand \citeyearpar { o o m } { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { suppress-author } } \NewDocumentCommand \fullcite { o o m } { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { full-cite } } \seq_new:N \l__csl_cite_keys_seq \seq_new:N \l__csl_citation_items_seq \prop_new:N \l__csl_citation_properties_prop \prop_new:N \l__csl_citation_info_prop % #1: prenote % #2: postnote % #3: cite keys % #4: mode \cs_new:Npn \__csl_cite_in_text:nnnn #1#2#3#4 { \tl_if_blank:nTF {#3} { \__csl_print_undefined_citation:n {#3} } { \__csl_cite_init: \__csl_collect_citation_items:nnn {#1} {#2} {#3} \__csl_process_citation_info: \prop_put:Nnn \l__csl_citation_properties_prop { noteIndex } { 0 } \tl_if_empty:nF {#4} { \prop_put:Nnn \l__csl_citation_properties_prop { mode } {#4} } \__csl_make_citation: } } % #1: prenote % #2: postnote % #3: cite keys % #4: mode \cs_new:Npn \__csl_cite_note:nnnn #1#2#3#4 { \bool_if:NTF \l__csl_in_note_bool { \tl_if_blank:nTF {#3} { \__csl_print_undefined_citation:n {#3} } { \__csl_cite_init: \__csl_collect_citation_items:nnn {#1} {#2} {#3} \__csl_process_citation_info: \tl_if_empty:nF {#4} { \prop_put:Nnn \l__csl_citation_properties_prop { mode } {#4} } \__csl_make_citation: } } { \footnote { \tl_if_blank:nTF {#3} { \__csl_print_undefined_citation:n {#3} } { \__csl_cite_init: \__csl_collect_citation_items:nnn {#1} {#2} {#3} \__csl_process_citation_info: \tl_if_empty:nF {#4} { \prop_put:Nnn \l__csl_citation_properties_prop { mode } {#4} } \__csl_make_citation: } } } } % #1: prenote % #2: postnote % #3: cite keys \cs_new:Npn \__csl_cite_parens:nnn #1#2#3 { \bool_if:NTF \l__csl_note_bool { ( \__csl_cite_in_text:nnnn {#1} {#2} {#3} { } ) } { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { } } } \cs_new:Npn \__csl_text_cite:nnn #1#2#3 { \bool_if:NTF \l__csl_note_bool { % In note styles, the authors are printed in-text followed by a note. \__csl_cite_in_text:nnnn {#1} {#2} {#3} { author-only } \__csl_cite_note:nnnn {#1} {#2} {#3} { } } { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { composite } } } \bool_new:N \l__csl_multi_cite_bool \cs_new:Npn \__csl_cites: { \__csl_cite_init: \bool_set_true:N \l__csl_multi_cite_bool \__csl_next_cites:nnn } \NewDocumentCommand \__csl_next_cites:nnn { o o g } { \tl_if_novalue:nTF {#3} { \bool_if:NTF \l__csl_note_bool { \footnote { \__csl_process_citation_info: \__csl_make_citation: } } { \__csl_process_citation_info: \__csl_make_citation: } } { \__csl_collect_citation_items:nnn {#1} {#2} {#3} \__csl_next_cites:nnn } } \cs_new:Npn \__csl_cite_init: { \bool_set_false:N \l__csl_multi_cite_bool \prop_clear:N \l__csl_citation_info_prop \seq_clear:N \l__csl_cite_keys_seq \seq_clear:N \l__csl_citation_items_seq \prop_clear:N \l__csl_citation_properties_prop \tl_clear:N \l__csl_cite_prefix_tl \tl_clear:N \l__csl_cite_suffix_tl } % Appends the cite key into \l__csl_cite_keys_seq and cite-items into % \l__csl_citation_items_seq % #1, #2: prenote/postnote % #3: keys \cs_new:Npn \__csl_collect_citation_items:nnn #1#2#3 { \tl_if_novalue:nTF {#2} { \tl_if_novalue:nTF {#1} { \__csl_process_cite_input_aux:nnn { } { } {#3} } { \__csl_process_cite_input_aux:nnn { } {#1} {#3} } } { \__csl_process_cite_input_aux:nnn {#1} {#2} {#3} } } \cs_new:Npn \__csl_process_cite_input_aux:nnn #1#2#3 % #1: prenote, #2: postnote, #3: keys % Return: "{id={ITEM-1},{locator=6},...}, {id={ITEM-2},...}, ..." { \int_zero:N \l_tmpa_int \clist_map_inline:nn {#3} { \int_incr:N \l_tmpa_int \seq_put_right:Nn \l__csl_cite_keys_seq {##1} \int_compare:nNnTF { \l_tmpa_int } = { 1 } { \__csl_process_cite_item:nnn {#1} {#2} {##1} } { \__csl_process_cite_item:nnn { } { } {##1} } } } \prop_new:N \l__csl_cite_item_prop % Collect citation items into \l__csl_citation_items_seq % Append "{id={ITEM},locator={42},label={page}}" into \l__csl_citation_items_seq % #1: prenote % #2: postnote % #3: key \cs_new:Npn \__csl_process_cite_item:nnn #1#2#3 { \prop_clear:N \l__csl_cite_item_prop \prop_put:Nnn \l__csl_cite_item_prop { id } {#3} % \bool_if:T \l__csl_suppress_author_bool % { \prop_put:Nnn \l__csl_cite_item_prop { suppress-author } { true } } % \bool_if:T \l__csl_author_only_bool % { \prop_put:Nnn \l__csl_cite_item_prop { author-only } { true } } \tl_clear:N \l__csl_cite_prefix_tl \tl_clear:N \l__csl_cite_suffix_tl \tl_if_empty:nF {#1} { \tl_set:Nn \l__csl_cite_prefix_tl {#1} \tl_put_right:NV \l__csl_cite_prefix_tl \l__csl_prefix_separator_tl } \tl_if_empty:nF {#2} { \tl_if_in:nnTF {#2} { = } { \keys_set:nn { csl / cite-item } {#2} } { \regex_match:nnTF { \d+ } {#2} { \__csl_set_locator:nn { page } {#2} } { \tl_set:Nn \l__csl_cite_suffix_tl {#2} \tl_put_left:NV \l__csl_cite_suffix_tl \l__csl_suffix_separator_tl } } } % The affixes are treated as cite item affixes in a multi-cite command % (like `\cites`). Otherwise they are citation affixes. \bool_if:NTF \l__csl_multi_cite_bool { \tl_if_empty:NF \l__csl_cite_prefix_tl { \prop_put:NnV \l__csl_cite_item_prop { prefix } \l__csl_cite_prefix_tl } \tl_if_empty:NF \l__csl_cite_suffix_tl { \prop_put:NnV \l__csl_cite_item_prop { suffix } \l__csl_cite_suffix_tl } } { \tl_if_empty:NF \l__csl_cite_prefix_tl { \prop_put:NnV \l__csl_citation_properties_prop { prefix } \l__csl_cite_prefix_tl } \tl_if_empty:NF \l__csl_cite_suffix_tl { \prop_put:NnV \l__csl_citation_properties_prop { suffix } \l__csl_cite_suffix_tl } } \__csl_serialize_prop:NN \l__csl_cite_item_prop \l_tmpa_tl \tl_put_left:NV \l_tmpa_tl { \c_left_brace_str } \tl_put_right:NV \l_tmpa_tl { \c_right_brace_str } \seq_put_right:NV \l__csl_citation_items_seq \l_tmpa_tl } \cs_new:Npn \__csl_set_locator:nn #1#2 { \tl_if_empty:nTF {#2} { \msg_warning:nnn { citation-style-language } { empty-locator } {#1} } { \prop_put:Nnn \l__csl_cite_item_prop { label } {#1} \prop_put:Nnn \l__csl_cite_item_prop { locator } {#2} } } \msg_new:nnn { citation-style-language } { empty-locator } { Empty~ '#1'~ locator. } \tl_new:N \l__csl_cite_prefix_tl \tl_new:N \l__csl_cite_suffix_tl \keys_define:nn { csl / cite-item } { prefix .tl_set:N = \l__csl_cite_prefix_tl, suffix .tl_set:N = \l__csl_cite_suffix_tl, locator .prop_put:N = \l__csl_cite_item_prop, label .prop_put:N = \l__csl_cite_item_prop, suppress-author .prop_put:N = \l__csl_cite_item_prop, author-only .prop_put:N = \l__csl_cite_item_prop, uris .prop_put:N = \l__csl_cite_item_prop, % Locators. act .code:n = { \__csl_set_locator:nn { act } {#1} } , appendix .code:n = { \__csl_set_locator:nn { appendix } {#1} } , article .code:n = { \__csl_set_locator:nn { article-locator } {#1} } , book .code:n = { \__csl_set_locator:nn { book } {#1} } , canon .code:n = { \__csl_set_locator:nn { canon } {#1} } , chapter .code:n = { \__csl_set_locator:nn { chapter } {#1} } , column .code:n = { \__csl_set_locator:nn { column } {#1} } , elocation .code:n = { \__csl_set_locator:nn { elocation } {#1} } , equation .code:n = { \__csl_set_locator:nn { equation } {#1} } , figure .code:n = { \__csl_set_locator:nn { figure } {#1} } , folio .code:n = { \__csl_set_locator:nn { folio } {#1} } , issue .code:n = { \__csl_set_locator:nn { issue } {#1} } , line .code:n = { \__csl_set_locator:nn { line } {#1} } , note .code:n = { \__csl_set_locator:nn { note } {#1} } , opus .code:n = { \__csl_set_locator:nn { opus } {#1} } , page .code:n = { \__csl_set_locator:nn { page } {#1} } , paragraph .code:n = { \__csl_set_locator:nn { paragraph } {#1} } , part .code:n = { \__csl_set_locator:nn { part } {#1} } , rule .code:n = { \__csl_set_locator:nn { rule } {#1} } , scene .code:n = { \__csl_set_locator:nn { scene } {#1} } , section .code:n = { \__csl_set_locator:nn { section } {#1} } , sub-verbo .code:n = { \__csl_set_locator:nn { sub-verbo } {#1} } , supplement .code:n = { \__csl_set_locator:nn { supplement } {#1} } , table .code:n = { \__csl_set_locator:nn { table } {#1} } , timestamp .code:n = { \__csl_set_locator:nn { timestamp } {#1} } , title .code:n = { \__csl_set_locator:nn { title-locator } {#1} } , verse .code:n = { \__csl_set_locator:nn { verse } {#1} } , version .code:n = { \__csl_set_locator:nn { version } {#1} } , volume .code:n = { \__csl_set_locator:nn { volume } {#1} } , % Citation properties infix .prop_put:N = \l__csl_citation_properties_prop, } \tl_new:N \l__csl_citation_id_tl \tl_new:N \l__csl_cite_items_tl \tl_new:N \l__csl_note_index_tl % Load the cite keys and prepare: % - \l__csl_citation_id_tl % - \l__csl_citation_properties_prop \cs_new:Npn \__csl_process_citation_info: { \__csl_process_citation_id: \__csl_get_note_index: \prop_put:NnV \l__csl_citation_properties_prop { noteIndex } \l__csl_note_index_tl \__csl_make_chapter_property: \__csl_add_back_ref_info: } \cs_new:Npn \__csl_make_chapter_property: { \int_if_exist:NT \c@chapter { \prop_put:Nnx \l__csl_citation_properties_prop { chapterIndex } { \int_use:N \c@chapter } } } \tl_new:N \l__csl_back_ref_tl \prop_new:N \g__csl_back_ref_info_prop \prop_new:N \l__csl_back_ref_section_pop % Provide empty \@currentHref when hyperref is not loaded. % LaTeX2e 2023-06-01 defines `\@currentHref` in the kernel. \cs_if_exist:NF \@currentHref { \cs_new:Npn \@currentHref {} } % TODO: write backref info to .brf file or .aux file \cs_new:Npn \__csl_add_back_ref_info: { % Same as the second argument of backref's \backcite % \thepage: the page number % \@currentlabel: the current label of the citation % \@currentHref: the current anchor name \tl_if_empty:NTF \@currentlabel { \tl_set:Nx \l__csl_back_ref_tl { { \thepage } { (document) } { Doc-Start } } } { \tl_set:Nx \l__csl_back_ref_tl { { \thepage } { \@currentlabel } { \@currentHref } } } \seq_map_inline:Nn \l__csl_cite_keys_seq { \prop_get:NnNTF \g__csl_back_ref_info_prop {##1} \l_tmpa_tl { \tl_put_right:Nn \l_tmpa_tl { , } \tl_put_right:NV \l_tmpa_tl \l__csl_back_ref_tl \prop_gput:NnV \g__csl_back_ref_info_prop {##1} \l_tmpa_tl } { \prop_gput:NnV \g__csl_back_ref_info_prop {##1} \l__csl_back_ref_tl } } } \tl_new:N \l__csl_citation_info_tl \tl_new:N \l__csl_citation_tl \prop_new:N \g__csl_citations_prop \tl_new:N \l__csl_citation_properties_tl % Write citation info to aux and print the citation contents. \cs_new:Npn \__csl_make_citation: { \prop_clear:N \l__csl_citation_info_prop % citationID \prop_put:NnV \l__csl_citation_info_prop { citationID } \l__csl_citation_id_tl % citationItems \__csl_serialize_seq:NN \l__csl_citation_items_seq \l__csl_cite_items_tl \prop_put:NnV \l__csl_citation_info_prop { citationItems } \l__csl_cite_items_tl % properties \__csl_serialize_prop:NN \l__csl_citation_properties_prop \l__csl_citation_properties_tl \prop_put:NnV \l__csl_citation_info_prop { properties } \l__csl_citation_properties_tl \__csl_serialize_prop:NN \l__csl_citation_info_prop \l__csl_citation_info_tl % Write to .aux file % \tl_show:N \l__csl_citation_info_tl \exp_args:NVV \__csl_write_aux_citation:nn \g__csl_ref_section_index_int \l__csl_citation_info_tl \bool_if:NT \l__csl_regression_test_bool { \tl_show:N \l__csl_citation_info_tl } % Print the citation string \prop_get:NVNTF \g__csl_citations_prop \l__csl_citation_id_tl \l__csl_citation_tl { \__csl_print_citation: } { \bool_if:NTF \l__csl_engine_initialized_bool { % \tl_show:N \l__csl_citation_info_tl % \tl_set:Nf \l__csl_citation_tl % { \exp_args:NV \__csl_cite_aux:n \l__csl_citation_info_tl } \group_begin: \char_set_catcode_other:N \% \char_set_catcode_other:N \# \exp_args:NV \__csl_cite_aux:n \l__csl_citation_info_tl \__csl_print_citation: \group_end: } { \exp_args:Nx \__csl_print_undefined_citation:n { \seq_use:Nn \l__csl_cite_keys_seq { ,~ } } } } } \cs_new:Npn \__csl_cite_aux:n #1 { \lua_now:e { csl_citation_manager:cite("\lua_escape:n {#1}") } } % #1: seq % #2: tl \cs_new:Npn \__csl_serialize_seq:NN #1#2 { \tl_clear:N #2 \seq_map_inline:Nn #1 { \tl_if_empty:NF #2 { \tl_put_right:Nn #2 { , } } \tl_put_right:Nn #2 { ##1 } } } % #1: prop % #2: tl \cs_new:Npn \__csl_serialize_prop:NN #1#2 { \tl_clear:N #2 \prop_map_inline:Nn #1 { \tl_if_empty:NF #2 { \tl_put_right:Nn #2 { , } } \tl_put_right:Nn #2 { ##1 = { ##2 } } } } \tl_new:N \l__csl_cite_keys_tl \tl_new:N \l__csl_citation_count_tl \int_new:N \l__csl_citation_count_int \prop_new:N \g__csl_citations_count_prop % Load cite keys from `\l__csl_cite_keys_seq` and make `\l__csl_citation_id_tl`. % Set \l__csl_citation_id_tl = "ITEM-1,ITEM-2@4". \cs_new:Npn \__csl_process_citation_id: { \tl_set:Nx \l__csl_cite_keys_tl { \seq_use:Nn \l__csl_cite_keys_seq { , } } % \prop_show:N \g__csl_citations_count_prop % \tl_show:N \l__csl_cite_keys_tl \prop_get:NVNTF \g__csl_citations_count_prop \l__csl_cite_keys_tl \l__csl_citation_count_tl { \int_set:Nn \l__csl_citation_count_int { \l__csl_citation_count_tl } \int_incr:N \l__csl_citation_count_int } { \int_set_eq:NN \l__csl_citation_count_int \c_one_int } \prop_gput:NVV \g__csl_citations_count_prop \l__csl_cite_keys_tl \l__csl_citation_count_int \tl_set:Nx \l__csl_citation_id_tl { \l__csl_cite_keys_tl @ \int_use:N \l__csl_citation_count_int } } \int_new:N \g__csl_pseudo_note_index_int \int_gset:Nn \g__csl_pseudo_note_index_int { 0 } % Save the note number to \l__csl_note_index_tl % TODO: multiple citations in a note \cs_new:Npn \__csl_get_note_index: % #1: \l__csl_note_index_tl { \bool_if:NTF \l__csl_note_bool { \tl_set:Nx \l__csl_note_index_tl { \int_use:c { c@ \@mpfn } } } { \tl_if_empty:NTF \l__csl_class_tl { % The style class (in-text/note) is undetermined. \int_set_eq:Nc \l_tmpa_int { c@ \@mpfn } \int_gincr:N \g__csl_pseudo_note_index_int \int_add:Nn \l_tmpa_int { \g__csl_pseudo_note_index_int } \tl_set:Nx \l__csl_note_index_tl { \int_use:N \l_tmpa_int } } { \tl_set:Nn \l__csl_note_index_tl { 0 } } } } \cs_new:Npn \__csl_write_aux_citation:nn #1#2 % #1: refsection index (already converted to tl) % #2: citation info "{}{{id=ITEM-1},{id=ITEM-2}}{}" { \if@filesw \iow_now:Nn \@auxout { \csl@aux@cite {#1} {#2} } \fi } % Should be set to true when located in a footnote, and to false otherwise \bool_new:N \l__csl_in_note_bool \hook_gput_code:nnn { cmd / @makefntext / before } { . } { \bool_set_true:N \l__csl_in_note_bool } \hook_gput_code:nnn { cmd / @makefntext / after } { . } { \bool_set_false:N \l__csl_in_note_bool } % #1: \l__csl_citation_tl \cs_new:Npn \__csl_print_citation: { \bool_if:NT \l__csl_regression_test_bool { \tl_show:N \l__csl_citation_tl } \l__csl_citation_tl } \cs_new:Npn \__csl_print_undefined_citation:n #1 % #1: keys { \tl_if_blank:nTF {#1} { \__csl_warn_citation_undefined:n { } \__csl_set_undefined_cite:n { ? } } { \clist_map_inline:nn {#1} { \__csl_warn_citation_undefined:n {##1} } % Underscores in citation keys like `zankl_kunstliche_2019` may cause a missing $ error. % Thus We convert them to str. \tl_set:Nx \l_tmpa_str { \tl_to_str:n {#1} } \exp_args:NV \__csl_set_undefined_cite:n \l_tmpa_str } \bool_if:NT \l__csl_regression_test_bool { \tl_show:N \l__csl_citation_tl } \group_begin: \reset@font \l__csl_citation_tl \group_end: } \cs_new:Npn \__csl_set_undefined_cite:n #1 { \tl_set:Nn \l__csl_citation_tl { [ \textbf {#1} ] } } % \msg_new:nnn { citation-style-language } { undefined-citation } % { Citation~ '#1'~ on~ page~ \thepage \space undefined~ \msg_line_context: . } \cs_new:Npn \__csl_warn_citation_undefined:n #1 { \G@refundefinedtrue % The warning message is read by latexmk. \@latex@warning {Citation~ `#1'~ on~ page~ \thepage \space undefined} } \DeclareDocumentCommand \nocite { m } { \__csl_no_cite:n {#1} } \cs_new:Npn \__csl_no_cite:n #1 { \__csl_cite_init: \__csl_collect_citation_items:nnn { } { } {#1} \tl_set:Nx \l__csl_cite_items_tl { \seq_use:Nn \l__csl_citation_items_seq { , } } \prop_clear:N \l__csl_citation_properties_prop \prop_put:Nnn \l__csl_citation_properties_prop { noteIndex } { 0 } \__csl_make_chapter_property: \__csl_serialize_prop:NN \l__csl_citation_properties_prop \l__csl_citation_properties_tl \tl_set:Nx \l__csl_citation_info_tl { citationID = { @nocite } , citationItems = { \tl_use:N \l__csl_cite_items_tl } , properties = { \tl_use:N \l__csl_citation_properties_tl } } \bool_if:NT \l__csl_regression_test_bool { \tl_show:N \l__csl_citation_info_tl } \exp_args:NVV \__csl_no_cite_write_aux:nn \g__csl_ref_section_index_int \l__csl_citation_info_tl \sys_if_engine_luatex:T { \lua_now:n { csl_citation_manager:nocite("#1") } } \tl_clear:N \l__csl_citation_tl \bool_if:NT \l__csl_regression_test_bool { \tl_show:N \l__csl_citation_tl } } \cs_new:Npn \__csl_no_cite_write_aux:nn #1#2 { \__csl_if_preamble:TF { \hook_gput_code:nnn { begindocument } { . } { \__csl_write_aux_citation:nn {#1} {#2} } } { \__csl_write_aux_citation:nn {#1} {#2} } } \prg_new_conditional:Nnn \__csl_if_preamble: { T , F , TF } { \if_meaning:w \@begindocumenthook \@undefined \prg_return_false: \else \prg_return_true: \fi } % Used in aux files to register cite items. % #1: a citation object \cs_set:Npn \csl@aux@cite #1#2 { \sys_if_engine_luatex:T { \lua_now:e { csl_citation_manager:register_citation_info(#1, "\lua_escape:n {#2}") } } } \cs_new:Npn \cslcitation #1#2 { \prop_gput:Nnn \g__csl_citations_prop {#1} {#2} } % This command is for use with hyperref. % #1: cite id % #2: cite contents \cs_new:Npn \cslcite #1#2 {#2} % This command is for use with hyperref. % #1: cite id \cs_new:Npn \cslundefinedcite #1 { \textbf {#1} \__csl_warn_citation_undefined:n {#1} }