\NeedsTeXFormat{LaTeX2e} \ProvidesExplPackage{PrimeTeX/packages/CrossRef}{2025-09-16}{1.0}{ Custom label and reference commands. } %<@@=CrossRef> \debug_on:n {all} \prop_const_from_keyval:Nn \c_@@_section_names_prop { 1 = Part, 2 = Chapter, 3 = Section, 4 = Subsection, 5 = Sub-subsection, 6 = Paragraph, 7 = Subparagraph, 8 = Appendix, } \prop_const_from_keyval:Nn \c_@@_section_levels_prop { part = 0, chapter = 1, section = 2, subsection = 3, subsubsection = 4, paragraph = 5, subparagraph = 6, } \str_const:Nn \c_@@_label_delimiter_str {/} \str_const:Nn \c_@@_label_parent_str {.} \str_const:Nn \c_@@_label_placeholder_str {*} \str_const:Nn \c_@@_appendices_label_str {appendices} \seq_new:N \g_@@_label_seq \seq_gclear:N \g_@@_label_seq \NewDocumentCommand{\Part}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {part} {#1} {#2} {#3} {#4} \RenewDocumentCommand{\parttitle}{}{#3} } \NewDocumentCommand{\Chapter}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {chapter} {#1} {#2} {#3} {#4} } \NewDocumentCommand{\Section}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {section} {#1} {#2} {#3} {#4} } \NewDocumentCommand{\Subsection}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {subsection} {#1} {#2} {#3} {#4} } \NewDocumentCommand{\Subsubsection}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {subsubsection} {#1} {#2} {#3} {#4} } \NewDocumentCommand{\Paragraph}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {paragraph} {#1} {#2} {#3} {#4} } \NewDocumentCommand{\Subparagraph}{s t^ m >{\TrimSpaces}m}{ \@@_heading:nnnnn {subparagraph} {#1} {#2} {#3} {#4} } \NewDocumentCommand{\Appendices}{t^}{ \@@_heading:nnnnn {part} {\c_true_bool} {#1} {Appendices} { \c_@@_appendices_label_str } } \NewDocumentCommand{\GetCurrentLabel}{}{ \seq_use:Nn \g_@@_label_seq {\c_@@_label_delimiter_str} } \NewDocumentCommand{\SetCurrentLabel}{ % #1: Delimiter O{\c_@@_label_delimiter_str} % #2: Label string. >{\TrimSpaces}m }{ \seq_set_split:Nnn \g_@@_label_seq {#1} {#2} } \str_new:N \l_@@_esc_parent_str \exp_args:Nnne \str_concat:NNN \l_@@_esc_parent_str \c_backslash_str \c_@@_label_parent_str \str_new:N \l_@@_esc_placeholder_str \exp_args:Nnne \str_concat:NNN \l_@@_esc_placeholder_str \c_backslash_str \c_@@_label_placeholder_str % MATCH delimiter, parent, and placeholder symbols, but CAPTURE only % parents and placeholders, NOT delimiters. \exp_args:Nne \regex_const:Nn \c_@@_ref_regex {\c_@@_label_delimiter_str|(\l_@@_esc_parent_str|\l_@@_esc_placeholder_str)} \regex_log:N \c_@@_ref_regex \seq_new:N \l_@@_label_seq \seq_new:N \l_@@_ref_seq \str_new:N \l_@@_ref_str \str_new:N \l_@@_section_name_str \keys_define:nn {HiRef} { page .bool_set:N = \l_@@_page_bool, } \NewDocumentCommand{\HiRef}{ % #1: Star (No star = include section name in reference, star = do not include). s % #2: Key-value arguments. O{} % #3: Label Reference. >{\TrimSpaces}m % #4: Link text. O{} }{ \group_begin: \keys_set:nn {HiRef} {#2} %\prop_show:N \c_@@_section_names_prop %\str_set:Ne \l_@@_ref_str {#2} \@@_HiRef_process:Nn \l_@@_ref_str {#3} \str_set:Nn \l_@@_section_name_str {} % Create a reference with the string we just created. \hyperref [\l_@@_ref_str] { \str_if_empty:nTF {#4} { \bool_if:NTF \l_@@_page_bool { \bool_if:nF {#1} {Page}~\pageref* {\l_@@_ref_str} } { % Output the section name only if no star is passed. \bool_if:nF {#1} { \str_if_eq:eeTF { \seq_item:Nn \l_@@_ref_seq {1} } { \c_@@_appendices_label_str } { % If the part label is "appendices", the section name is "Appendix". \str_set:Ne \l_@@_section_name_str { \prop_item:Nn \c_@@_section_names_prop {8} } } { % Otherwise, look up the correct section name. \str_set:Ne \l_@@_section_name_str { \prop_item:Ne \c_@@_section_names_prop { \int_eval:n{\seq_count:N \l_@@_ref_seq} } } } } \str_if_empty:NF \l_@@_section_name_str { \l_@@_section_name_str \nobreakspace } \ref* {\l_@@_ref_str} } } { #4 } } \group_end: } % Deprecated in favor of \HiRef{label}[text] % \NewDocumentCommand{\HiperRef}{ % % #1: Label reference. % >{\TrimSpaces}m % % #2: Display text. % m % }{ \group_begin: % %\str_set:Ne \l_@@_ref_str {#1} % \@@_HiRef_process:Nn \l_@@_ref_str {#1} % \hyperref[\l_@@_ref_str]{#2} % \group_end: } \cs_new:Nn \@@_HiRef_process:Nn { % Split the string on the delimiters. \exp_args:NNNe \seq_set_regex_split:NNn \l_@@_ref_seq \c_@@_ref_regex {#2} % The regex split creates a lot of empty items, so remove them. \seq_remove_all:Nn \l_@@_ref_seq {} % Log the input and output of the split process for debugging. \str_log:n {#2} \seq_log:N \l_@@_ref_seq % Copy the current label sequence to a local variable. \seq_set_eq:NN \l_@@_label_seq \g_@@_label_seq \seq_log:N \l_@@_label_seq % Iterate through the reference sequence, removing one element from the % end of the temporary label sequence for each parent symbol. \seq_map_indexed_function:NN \l_@@_ref_seq \@@_seq_map:nn \str_set:Ne \l_@@_ref_head_str {\str_head:n {#2}} \str_log:N \l_@@_ref_head_str % If the reference is relative (i.e. does not start with / or *), add the % shortened temporary label sequence to the beginning of the reference % sequence. \exp_args:Ne \bool_if:nF { \str_if_eq_p:NN \l_@@_ref_head_str \c_@@_label_delimiter_str || \str_if_eq_p:NN \l_@@_ref_head_str \c_@@_label_placeholder_str } { \seq_concat:NNN \l_@@_ref_seq \l_@@_label_seq \l_@@_ref_seq } \seq_log:N \l_@@_ref_seq % Join the reference sequence back into a delimited string. \str_set:Ne \l_@@_ref_str { \seq_use:Ne \l_@@_ref_seq {\c_@@_label_delimiter_str} } \str_if_eq:eeF {\str_head:N \l_@@_ref_str} {\c_@@_label_delimiter_str} { \str_put_left:Ne \l_@@_ref_str {\c_@@_label_delimiter_str} } \str_set_eq:NN #1 \l_@@_ref_str } \tl_new:N \l_@@_trash_tl \cs_new:Nn \@@_seq_map:nn { \str_case_e:nn {#2} { {\c_@@_label_parent_str} { \seq_pop_right:NN \l_@@_label_seq \l_@@_trash_tl \seq_pop_left:NN \l_@@_ref_seq \l_@@_trash_tl } {\c_@@_label_placeholder_str} { %\message{wildcard~map} \seq_pop_left:NN \l_@@_label_seq \l_tmpa_tl % \seq_pop_left:NN \l_@@_ref_seq \l_@@_trash_tl % \seq_put_left:NN \l_@@_ref_seq \l_tmpa_tl \exp_args:NNne \seq_set_item:Nnn \l_@@_ref_seq {#1} {\l_tmpa_tl} } } } \int_new:N \l_@@_current_level_int \int_new:N \l_@@_pop_count_int \int_new:N \l_@@_pop_index_int \str_new:N \l_@@_label_str \cs_new:Nn \@@_heading:nnnnn { \group_begin: % #1: Section name: part, chapter, section, etc. % #2: Star (passed to default sectioning command) % #3: ^ token: suppresses sectioning command. % #4: Section title % #5: Label \debug_on:n {all} % Get the numeric level of the section command. \prop_get:NnN \c_@@_section_levels_prop {#1} \l_@@_current_level_int % \message{^^J@@_heading:~#5~\seq_use:Ne \g_@@_label_seq {\c_@@_label_delimiter_str}} \seq_set_eq:NN \l_@@_label_seq \g_@@_label_seq \int_set:Nn \l_@@_pop_count_int { \seq_count:N \l_@@_label_seq - \l_@@_current_level_int } \int_while_do:nNnn {\l_@@_pop_index_int} < {\l_@@_pop_count_int} { \seq_if_empty:NF \l_@@_label_seq { \seq_pop_right:NN \l_@@_label_seq \l_tmpa_tl } \int_incr:N \l_@@_pop_index_int } % Add the new label to the end of the modified label sequence. \seq_put_right:Ne \l_@@_label_seq {#5} \seq_gset_eq:NN \g_@@_label_seq \l_@@_label_seq % Join the label sequence into a string. \str_set:Ne \l_@@_label_str { \seq_use:Ne \l_@@_label_seq {\c_@@_label_delimiter_str} } % Add a delimiter to the beginning of the label string to indicate this is % an absolute reference. \str_put_left:Ne \l_@@_label_str {\c_@@_label_delimiter_str} % If the caret argument is NOT present, output the heading. \bool_if:nF {#3} { % Pass along the star argument if it is present. \bool_if:nTF {#2} { \use:c {#1} * {#4} } { \use:c {#1} {#4} } } % Output the label. \exp_args:Ne \label {\l_@@_label_str} \group_end: } \debug_off:n {all}