% Maintained by Matthew Bertucci, 2024-present % Please report all issues and feature requests at https://github.com/mbertucci47/keytheorems % This work is licensed under the LPPL version 1.3c or later: https://www.latex-project.org/lppl.txt \NeedsTeXFormat{LaTeX2e}[2023-06-01] \ProvidesExplPackage{keytheorems}{2024-09-22}{0.1.4}{l3keys interface to amsthm} \IfFormatAtLeastTF { 2024-06-01 } { } { \RequirePackage{nameref} % to avoid ltproperties in code below \cs_generate_variant:Nn \cs_set:Npn { Npe } \cs_generate_variant:Nn \iow_shipout:Nn { Ne } \cs_generate_variant:Nn \keys_set:nn { ne } \cs_generate_variant:Nn \msg_warning:nnn { nne } \cs_generate_variant:Nn \tl_put_right:Nn { Ne } \ProvideDocumentCommand \IfPackageLoadedT { m m } { \IfPackageLoadedTF{#1}{#2}{ } } \ProvideDocumentCommand \IfPackageLoadedF { m m } { \IfPackageLoadedTF{#1}{ }{#2} } \ProvideDocumentCommand \IfClassLoadedT { m m } { \IfClassLoadedTF{#1}{#2}{ } } } \RequirePackage{aliascnt} % for sibling theorems \RequirePackage{amsthm} % ^ ams classes have way of ignoring this so don't need to check if they're loaded \RequirePackage{refcount} % for \getrefnumber \RequirePackage{translations} % for translating "List of Theorems" %%%%%%%%%%%%%%%%%%%%%% %%% Error Messages %%% %%%%%%%%%%%%%%%%%%%%%% \msg_new:nnn { keytheorems } { thmtools-before } { keytheorems~is~not~compatible~with~thmtools.~ Try~replacing~\protect\usepackage{thmtools}~with~ \protect\usepackage[thmtools-compat]{keytheorems}. } \msg_new:nnn { keytheorems } { thmtools-after } { keytheorems~is~not~compatible~with~thmtools.~ This~will~not~work~as~you~think!~ Try~replacing~\protect\usepackage{thmtools}~with~ \protect\usepackage[thmtools-compat]{keytheorems}. } \msg_new:nnn { keytheorems } { no-stored-theorem } { No~stored~theorem~'#1'~found!~ Try~compiling~again.~If~that~doesn't~work,~ check~the~spelling~of~'#1'. } \msg_new:nnn { keytheorems } { no-prop-for-stored-theorem } { No~property~'#1'~found~for~stored~theorem~'#2'. } \msg_new:nnn { keytheorems } { store-reversed-not-got } { 'store*=#1'~only~makes~sense~if~you've~called~\protect\getkeytheorem{#1}.~ Check~that~\protect\getkeytheorem{#1}~is~called~and~compile~again. } \msg_new:nnn { keytheorems } { restate-body-never-got } { With~'store*=#1',~\protect\getkeytheorem[body]{#1}~only~makes~sense~if~ \protect\getkeytheorem{#1}~has~been~called.~ Check~that~\protect\getkeytheorem{#1}~is~called~and~compile~again. } \msg_new:nnn { keytheorems } { store-reversed-called-twice } { With~'store*=#1',~\protect\getkeytheorem{#1}~can~be~called~only~once. } \msg_new:nnn { keytheorems } { undefined-thm-hook } { No~theorem~hook~'#1'.~Check~the~spelling.~ Should~be~one~of~'prehead',~'posthead',~'prefoot',~'postfoot',~or'restated'. } \msg_new:nnn { keytheorems } { hyperref-Autoref } { You~have~not~loaded~hyperref.~The~\protect\Autoref\space command~needs~ hyperref~to~work. } \msg_new:nnn { keytheorems } { no-Autorefname } { No~Autoref~name~for~'#1'.~ Please~define~\c_backslash_str #1Autorefname. } \msg_new:nnn { keytheorems } { thmstyle-undefined } { Theorem~style~'#1'~undefined.~ Use~\protect\newkeytheoremstyle\space instead. } \msg_new:nnn { keytheorems } { thmstyle-defined } { Theorem~style~'#1'~already~defined.~ Use~\protect\renewkeytheoremstyle\space instead. } \msg_new:nnn { keytheorems } { title-code-with-AMS } { The~'title-code'~key~has~no~effect~with~an~AMS~class. } % Error if thmtools loaded since compilation hangs. % If thmtools loaded after, produce warning. \IfPackageLoadedTF { thmtools } { \msg_fatal:nn { keytheorems } { thmtools-before } } { \hook_gput_code:nnn { package/thmtools/before } { . } { \msg_warning:nn { keytheorems } { thmtools-after } } } %%%%%%%%%%%%%%%%%%%%%%%%% %%% Declare Variables %%% %%%%%%%%%%%%%%%%%%%%%%%%% \tl_new:N \l__keythms_tmpa_tl \bool_new:N \g__keythms_listof_writefile_bool \bool_gset_false:N \g__keythms_listof_writefile_bool \bool_new:N \g__keythms_thmtoolscompat_bool \bool_gset_false:N \g__keythms_thmtoolscompat_bool \bool_new:N \l__keythms_listofheading_storereversed_bool \bool_new:N \l__keythms_thm_numbered_bool \bool_new:N \l__keythms_thm_unlessunique_bool \bool_new:N \l__keythms_thmuse_listhack_bool \bool_new:N \l__keythms_thmuse_restating_bool \clist_new:N \g__keythms_restatecounters_clist \clist_new:N \l__keythms_thmstyle_savedkeys_clist \iow_new:N \g__keythms_listof_stream \prop_new:N \g__keythms_thmnames_prop \prop_new:N \g__keythms_thmuse_othercounters_prop \prop_new:N \l__keythms_restate_counters_prop \tl_new:N \l__keythms_thm_currentthmstyle_tl \tl_new:N \l__keythms_thm_defaultkeys_tl \tl_new:N \l__keythms_thm_envname_tl \tl_new:N \l__keythms_thmstyle_defaultkeys_tl \tl_new:N \l__keythms_thmstyle_lnotebrace_tl \tl_new:N \l__keythms_thmstyle_rnotebrace_tl \tl_new:N \l_keythms_thmuse_envname_tl \tl_new:N \g__keythms_thmuse_temprestatedata_tl \tl_new:N \g__keythms_thmuse_temprestatedatareversed_tl \newcounter{keythms_restate_dummyctr} \cs_gset:Npn \theHkeythms_restate_dummyctr { restate.\arabic{keythms_restate_dummyctr} } \cs_gset:Npn \thekeythms_restate_dummyctr { } \newcounter{keythms_continues_dummyctr} \cs_gset:Npn \theHkeythms_continues_dummyctr { continues.\arabic{keythms_continues_dummyctr} } \cs_gset:Npn \thekeythms_continues_dummyctr { } \newcounter{keythms_unnumbered_dummyctr} \cs_gset:Npn \theHkeythms_unnumbered_dummyctr { unnumbered.\arabic{keythms_unnumbered_dummyctr} } \cs_gset:Npn \thekeythms_unnumbered_dummyctr { } \cs_generate_variant:Nn \hook_gput_code:nnn { nnV } \cs_generate_variant:Nn \keys_precompile:nnN { nv, nVc } %%%%%%%%%%%%%% %%% Styles %%% %%%%%%%%%%%%%% % \__keythms_thmstyle_setbraces:nn { } { } \cs_new_protected:Npn \__keythms_thmstyle_setbraces:nn #1#2 { \tl_set:Nn \l__keythms_thmstyle_lnotebrace_tl { #1 } \tl_set:Nn \l__keythms_thmstyle_rnotebrace_tl { #2 } } \cs_new_protected:Npn \keythms_thmstyle_savethmkey_reqval:n #1 { \clist_put_right:No \l__keythms_thmstyle_savedkeys_clist { \l_keys_key_str = { #1 } } } \cs_new_protected:Npn \keythms_thmstyle_savethmkey_optval:n #1 { \tl_if_empty:NTF \l_keys_value_tl { \clist_put_right:No \l__keythms_thmstyle_savedkeys_clist { \l_keys_key_str } } { \clist_put_right:No \l__keythms_thmstyle_savedkeys_clist { \l_keys_key_str = { #1 } } } } \keys_define:nn { keytheorems/thmstyle } { spaceabove .tl_set:N = \l__keythms_thmstyle_spaceabove_tl, spacebelow .tl_set:N = \l__keythms_thmstyle_spacebelow_tl, bodyfont .tl_set:N = \l__keythms_thmstyle_bodyfont_tl, headindent .tl_set:N = \l__keythms_thmstyle_headindent_tl, headfont .tl_set:N = \l__keythms_thmstyle_headfont_tl, headpunct .tl_set:N = \l__keythms_thmstyle_headpunct_tl, postheadspace .tl_set:N = \l__keythms_thmstyle_postheadspace_tl, break .meta:n = { postheadspace = \newline }, % add error if postheadspace set break .value_forbidden:n = true, numberfont .tl_set:N = \l__keythms_thmstyle_numberfont_tl, notefont .tl_set:N = \l__keythms_thmstyle_notefont_tl, notebraces .code:n = \exp_after:wN \__keythms_thmstyle_setbraces:nn #1, noteseparator .tl_set:N = \l__keythms_thmstyle_noteseparator_tl, headstyle .choice:, headstyle / margin .code:n = { \cs_set:Nn \keythms_thmstyle_headcmd:nnn { \makebox[0pt][r]{\NUMBER\ }\NAME\NOTE } }, headstyle / swapnumber .code:n = { \cs_set:Nn \keythms_thmstyle_headcmd:nnn { \NUMBER\ \NAME\NOTE } }, headstyle / unknown .cs_set:Np = \keythms_thmstyle_headcmd:nnn #1#2#3, headformat .meta:n = { headstyle = #1 }, inherit-style .choice:, inherit-style / plain .meta:n = {}, inherit-style / definition .meta:n = { bodyfont = \normalfont }, inherit-style / remark .meta:n = { headfont = \itshape, bodyfont = \normalfont, spaceabove = 0.5\topsep, spacebelow = 0.5\topsep, }, % thm keys that are saved for later numbered .code:n = \keythms_thmstyle_savethmkey_optval:n { #1 }, parent .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, numberwithin .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, within .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, sibling .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, numberlike .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, sharenumber .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, preheadhook .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, postheadhook .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, prefoothook .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, postfoothook .code:n = \keythms_thmstyle_savethmkey_reqval:n { #1 }, qed .code:n = \keythms_thmstyle_savethmkey_optval:n { #1 }, tcolorbox .code:n = \keythms_thmstyle_savethmkey_optval:n { #1 }, tcolorbox-no-titlebar .code:n = \keythms_thmstyle_savethmkey_optval:n { #1 }, } \cs_new_protected:Nn \keythms_thmstyle_thmname:n { \thmname{#1} } \cs_new_protected:Nn \keythms_thmstyle_thmnumber:n { \thmnumber{#1} } \cs_new_protected:Nn \keythms_thmstyle_thmnote:n { \thmnote{#1} } %% NOTE: if these are used, user is in charge of spacing with \NAME and \NUMBER %% QUESTION: should these be moved into def of \newkeytheoremstyle? \cs_new:Npn \NAME { \keythms_thmstyle_thmname:n { ##1 } } \cs_new:Npn \NUMBER { \keythms_thmstyle_thmnumber:n { \group_begin: \exp_not:V \l__keythms_thmstyle_numberfont_tl ##2 \group_end: } } \cs_new:Npn \NOTE { \keythms_thmstyle_thmnote:n { \exp_not:V \l__keythms_thmstyle_noteseparator_tl \group_begin: % group so notefont doesn't affect headpunct \exp_not:V \l__keythms_thmstyle_notefont_tl \l__keythms_thmstyle_lnotebrace_tl ##3 \l__keythms_thmstyle_rnotebrace_tl \group_end: } } \cs_new:Npn \keythms_thmstyle_headcmd_default:nnn #1#2#3 { \keythms_thmstyle_thmname:n { #1 } \keythms_thmstyle_thmnumber:n { ~ \group_begin: \exp_not:V \l__keythms_thmstyle_numberfont_tl #2 \group_end: } \keythms_thmstyle_thmnote:n { \exp_not:V \l__keythms_thmstyle_noteseparator_tl \group_begin: % group so notefont doesn't affect headpunct \exp_not:V \l__keythms_thmstyle_notefont_tl \l__keythms_thmstyle_lnotebrace_tl #3 \l__keythms_thmstyle_rnotebrace_tl \group_end: } } \cs_new_protected:Npn \keythms_thmstyle_setdefaultkeys:n #1 { \keys_precompile:nnN { keytheorems/thmstyle } { #1 } \l__keythms_thmstyle_defaultkeys_tl } \keythms_thmstyle_setdefaultkeys:n { % support files may overwrite these defaults spaceabove = \topsep, spacebelow = \topsep, bodyfont = \itshape, headindent = 0pt, headfont = \bfseries, headpunct = {.}, postheadspace = 5pt plus 1pt minus 1pt, numberfont = \upshape, notefont = \fontseries\mddefault\upshape, notebraces = {(}{)}, noteseparator = {~}, headstyle = \keythms_thmstyle_headcmd_default:nnn{#1}{#2}{#3}, } \NewDocumentCommand \newkeytheoremstyle { m m } { \cs_if_free:cTF { th@ #1 } { \keythms_thmstyle_declarestyle:nn { #1 } { #2 } } { \msg_error:nnn { keytheorems } { thmstyle-defined } { #1 } } } \NewDocumentCommand \renewkeytheoremstyle { m m } { \cs_if_free:cTF { th@ #1 } { \msg_error:nnn { keytheorems } { thmstyle-undefined } { #1 } } { \keythms_thmstyle_declarestyle:nn { #1 } { #2 } } } \NewDocumentCommand \providekeytheoremstyle { m m } { \cs_if_free:cT { th@ #1 } { \keythms_thmstyle_declarestyle:nn { #1 } { #2 } } } \NewDocumentCommand \declarekeytheoremstyle { m m } { \keythms_thmstyle_declarestyle:nn { #1 } { #2 } } \@onlypreamble \newkeytheoremstyle \@onlypreamble \renewkeytheoremstyle \@onlypreamble \providekeytheoremstyle \@onlypreamble \declarekeytheoremstyle \cs_new_eq:NN \keythms_thmstyle_new:nnnnnnnnn \newtheoremstyle \cs_generate_variant:Nn \keythms_thmstyle_new:nnnnnnnnn { nVVVVVVVe } \cs_new_protected:Npn \keythms_thmstyle_declarestyle:nn #1#2 { \clist_clear:N \l__keythms_thmstyle_savedkeys_clist \tl_use:N \l__keythms_thmstyle_defaultkeys_tl \keys_set:nn { keytheorems/thmstyle } { #2 } \keythms_thmstyle_new:nVVVVVVVe { #1 } \l__keythms_thmstyle_spaceabove_tl \l__keythms_thmstyle_spacebelow_tl \l__keythms_thmstyle_bodyfont_tl \l__keythms_thmstyle_headindent_tl \l__keythms_thmstyle_headfont_tl \l__keythms_thmstyle_headpunct_tl \l__keythms_thmstyle_postheadspace_tl { \text_expand:n { \keythms_thmstyle_headcmd:nnn{##1}{##2}{##3} } } % Define new inherit-style key \keys_define:nn { keytheorems/thmstyle } { inherit-style / #1 .meta:n = { #2 } } \tl_if_exist:cF { l__keythms_thmstyle_ #1 _savedkeys_tl } { \tl_new:c { l__keythms_thmstyle_ #1 _savedkeys_tl } } \keys_precompile:nVc { keytheorems/thm } \l__keythms_thmstyle_savedkeys_clist { l__keythms_thmstyle_ #1 _savedkeys_tl } } %%%%%%%%%%%%%%%%%%%%%%%%% %%% Defining Theorems %%% %%%%%%%%%%%%%%%%%%%%%%%%% % FIX: reimplement these without \NewDocumentCommand and \SplitArgument % \keythms_thm_setrefnames:n { } { or } \NewDocumentCommand \keythms_thm_setrefnames:nn { m >{\SplitArgument{1}{,}} m } { \__keythms_thm_setrefnames_aux:nnn{#1}#2 } \cs_new_protected:Npn \__keythms_thm_setrefnames_aux:nnn #1#2#3 { \cs_set:cpn { #1 autorefname } { #2 } \IfPackageLoadedT { cleveref } { \tl_if_novalue:nTF { #3 } { \crefname{#1}{#2}{\textbf{??~(pl.~#2)}} } { \crefname{#1}{#2}{#3} } } } \cs_generate_variant:Nn \keythms_thm_setrefnames:nn { nV } % \keythms_thm_setRefnames:n { } { or } \NewDocumentCommand \keythms_thm_setRefnames:nn { m >{\SplitArgument{1}{,}} m } { \__keythms_thm_setRefnames_aux:nnn{#1}#2 } \cs_new_protected:Npn \__keythms_thm_setRefnames_aux:nnn #1#2#3 { \cs_set:cpn { #1 Autorefname } { #2 } \IfPackageLoadedT { cleveref } { \tl_if_novalue:nTF { #3 } { \Crefname{#1}{#2}{\textbf{??~(pl.~#2)}} } { \Crefname{#1}{#2}{#3} } } } \cs_generate_variant:Nn \keythms_thm_setRefnames:nn { nV } \keys_define:nn { keytheorems/thm } { name .tl_set:N = \l__keythms_thm_name_tl, title .meta:n = { name = #1 }, heading .meta:n = { name = #1 }, refname .tl_set:N = \l__keythms_thm_refname_tl, Refname .tl_set:N = \l__keythms_thm_Refname_tl, numbered .choice:, numbered / true .code:n = \bool_set_true:N \l__keythms_thm_numbered_bool, numbered / false .code:n = \bool_set_false:N \l__keythms_thm_numbered_bool, numbered / yes .meta:n = { numbered = true }, numbered / no .meta:n = { numbered = false }, numbered / unless-unique .code:n = { \bool_set_true:N \l__keythms_thm_unlessunique_bool }, numbered / unless~unique .meta:n = { numbered = unless-unique }, numbered .default:n = true, parent .tl_set:N = \l__keythms_thm_parent_tl, numberwithin .meta:n = { parent = #1 }, within .meta:n = { parent = #1 }, sibling .tl_set:N = \l__keythms_thm_sibling_tl, numberlike .meta:n = { sibling = #1 }, sharenumber .meta:n = { sibling = #1 }, style .tl_set:N = \l__keythms_thm_style_tl, style .groups:n = { style-comes-first }, preheadhook .tl_set:N = \l__keythms_thm_preheadhook_tl, postheadhook .tl_set:N = \l__keythms_thm_postheadhook_tl, prefoothook .tl_set:N = \l__keythms_thm_prefoothook_tl, postfoothook .tl_set:N = \l__keythms_thm_postfoothook_tl, qed .tl_set:N = \l__keythms_thm_qed_tl, qed .default:n = \c_novalue_tl, % ^ distinguish between 'qed' and 'qed={}' tcolorbox .tl_set:N = \l__keythms_thm_tcbkeys_tl, tcolorbox .default:n = {}, tcolorbox-no-titlebar .meta:n = { tcolorbox={ notitle, before~upper={ \group_begin: \__keythms_thm_tcboxtemphead: \group_end: }, #1 } }, tcolorbox-no-titlebar .default:n = {}, } % what below is unnecessary? I really don't understand this code. \cs_new_protected:Npn \__keythms_thm_storedeferredthmhead:n #1 { \if@inlabel \indent \par \fi % eject a section head if one is pending \if@nobreak \adjust@parskip@nobreak \else \addpenalty\@beginparpenalty \addvspace\@topsep \addvspace{-\parskip} \fi % \global\@inlabeltrue % MY COMMENT: if this is uncommented then spacing after sections is wrong \everypar\dth@everypar \cs_set:Npn \__keythms_thm_tcboxtemphead: { \normalfont #1 } \ignorespaces } \keys_precompile:nnN { keytheorems/thm } { name = \q_no_value, refname = \q_no_value, Refname = \q_no_value, numbered = true, parent = {}, sibling = {}, style = {}, preheadhook = {}, postheadhook = {}, prefoothook = {}, postfoothook = {}, qed = \q_no_value, tcolorbox = \q_no_value, } \l__keythms_thm_defaultkeys_tl \cs_new_protected:Npn \__keythms_thm_makethmhooks:n #1 { \hook_new:n { keytheorems/#1/prehead } \hook_new:n { keytheorems/#1/posthead } \hook_new_reversed:n { keytheorems/#1/prefoot } \hook_new_reversed:n { keytheorems/#1/postfoot } \hook_new:n { keytheorems/#1/restated } } % Make generic theorem hooks \__keythms_thm_makethmhooks:n { allthms } % \newkeytheorem{}{} \NewDocumentCommand \newkeytheorem { m O{} } { \clist_map_inline:nn { #1 } % define multiple theorems at once { \keythms_thm_newkeythm:nn { ##1 } { #2 } } } \@onlypreamble \newkeytheorem % to prevent error when plain, remark, or definition style used \tl_new:N \l__keythms_thmstyle_plain_savedkeys_tl \tl_new:N \l__keythms_thmstyle_remark_savedkeys_tl \tl_new:N \l__keythms_thmstyle_definition_savedkeys_tl % \keythms_thm_newkeythm:nn { } { } \cs_new_protected:Npn \keythms_thm_newkeythm:nn #1#2 { % Store envname \tl_set:Nn \l__keythms_thm_envname_tl { #1 } % Make unless-unique false by default (can't precompile this) \bool_set_false:N \l__keythms_thm_unlessunique_bool % Set default keys \tl_use:N \l__keythms_thm_defaultkeys_tl % First set style so we can pick up additional thm keys, then overwrite if necessary \keys_set_groups:nnn { keytheorems/thm } { style-comes-first } { #2 } % Store theorem style \tl_set:Ne \l__keythms_thm_currentthmstyle_tl { \the\thm@style } \tl_if_empty:NTF \l__keythms_thm_style_tl { % if \theoremstyle set, also use saved thm keys \tl_if_exist:cT {l__keythms_thmstyle_ \l__keythms_thm_currentthmstyle_tl _savedkeys_tl} { \tl_use:c {l__keythms_thmstyle_ \l__keythms_thm_currentthmstyle_tl _savedkeys_tl} } } { % Temporarily set theorem style \__keythms_theoremstyle:n { \l__keythms_thm_style_tl } % If thm keys given in style, call now (possibly overwritten in next step) % but don't error if user uses a style defined with just \newtheoremstyle \tl_if_exist:cT { l__keythms_thmstyle_ \l__keythms_thm_style_tl _savedkeys_tl } { \tl_use:c { l__keythms_thmstyle_ \l__keythms_thm_style_tl _savedkeys_tl } } } % Set env-specific keys \keys_set:nn { keytheorems/thm } { #2 } % Set up env-specific hooks \__keythms_thm_makethmhooks:n { #1 } % Add to env-specific hooks (use label so code given in keys is outermost) % NOTE: faster to check if empty than add empty code to hook \tl_if_empty:NF \l__keythms_thm_preheadhook_tl { \hook_gput_code:nnV { keytheorems/#1/prehead } { keythms_hook_keys } \l__keythms_thm_preheadhook_tl } \tl_if_empty:NF \l__keythms_thm_postheadhook_tl { \hook_gput_code:nnV { keytheorems/#1/posthead } { keythms_hook_keys } \l__keythms_thm_postheadhook_tl } \tl_if_empty:NF \l__keythms_thm_prefoothook_tl { \hook_gput_code:nnV { keytheorems/#1/prefoot } { keythms_hook_keys } \l__keythms_thm_prefoothook_tl } \tl_if_empty:NF \l__keythms_thm_postfoothook_tl { \hook_gput_code:nnV { keytheorems/#1/postfoot } { keythms_hook_keys } \l__keythms_thm_postfoothook_tl } % Set name if none given \quark_if_no_value:NT \l__keythms_thm_name_tl % use quark so name={} is valid { % use e so \text_titlecase called only once per theorem definition, % not each time the theorem is used \tl_set:Ne \l__keythms_thm_name_tl { \text_titlecase_first:n { #1 } } } % associate formatted name with envname in prop list \prop_gput:NnV \g__keythms_thmnames_prop { #1 } \l__keythms_thm_name_tl % Call correct \newtheorem variant \bool_if:NTF \l__keythms_thm_unlessunique_bool { % [unq] is required since aux is read at begindocument % (technically right before) which is after theorem is defined \RequirePackage[unq]{unique} \tl_if_empty:NTF \l__keythms_thm_parent_tl { \hook_gput_code:nnn { keytheorems/#1/prehead } { keythms_hook_keys } { \setuniqmark { #1 } } \ifuniq{ #1 } { \bool_set_false:N \l__keythms_thm_numbered_bool } { \bool_set_true:N \l__keythms_thm_numbered_bool } \bool_if:NTF \l__keythms_thm_numbered_bool { \tl_if_empty:NTF \l__keythms_thm_sibling_tl { \__keythms_thm_new_numbered:nV { #1 } \l__keythms_thm_name_tl } { \exp_args:NnV \newaliascnt { #1 } \l__keythms_thm_sibling_tl \__keythms_thm_new_sibling:nVn { #1 } \l__keythms_thm_name_tl { #1 } \aliascntresetthe { #1 } } } { \__keythms_thm_new_unnumbered:nV { #1 } \l__keythms_thm_name_tl \hook_gput_code:nnn { keytheorems/#1/prehead } { keythms_hook_keys } { \keythms_if_restating:F { \refstepcounter{ keythms_unnumbered_dummyctr } } } } } { \__keythms_thm_new_uuwithparent:nVV { #1 } \l__keythms_thm_name_tl \l__keythms_thm_parent_tl } } { \bool_if:NTF \l__keythms_thm_numbered_bool { \tl_if_empty:NTF \l__keythms_thm_parent_tl { \tl_if_empty:NTF \l__keythms_thm_sibling_tl { \__keythms_thm_new_numbered:nV { #1 } \l__keythms_thm_name_tl } { \exp_args:NnV \newaliascnt { #1 } \l__keythms_thm_sibling_tl \__keythms_thm_new_sibling:nVn { #1 } \l__keythms_thm_name_tl { #1 } \aliascntresetthe { #1 } } } { \__keythms_thm_new_parent:nVV { #1 } \l__keythms_thm_name_tl \l__keythms_thm_parent_tl } } { \__keythms_thm_new_unnumbered:nV { #1 } \l__keythms_thm_name_tl \hook_gput_code:nnn { keytheorems/#1/prehead } { keythms_hook_keys } { \keythms_if_restating:F { \refstepcounter{ keythms_unnumbered_dummyctr } } } } } % Store theorem def and redefine it with keys \keythms_keyify_theorem:n { #1 } % define \autorefname and \Autorefname, might be redefined next \exp_args:NnV \cs_set:cpn { #1 autorefname } \l__keythms_thm_name_tl \exp_args:NnV \cs_set:cpn { #1 Autorefname } \l__keythms_thm_name_tl % Set ref names \quark_if_no_value:NF \l__keythms_thm_refname_tl { \keythms_thm_setrefnames:nV { #1 } \l__keythms_thm_refname_tl } \quark_if_no_value:NF \l__keythms_thm_Refname_tl { \keythms_thm_setRefnames:nV { #1 } \l__keythms_thm_Refname_tl } % Set up qed if needed \quark_if_no_value:NF \l__keythms_thm_qed_tl { \exp_args:Nno \__keythms_thm_qedcode:nn { #1 } { \l__keythms_thm_qed_tl } } % Set up tcolorbox if needed \quark_if_no_value:NF \l__keythms_thm_tcbkeys_tl { \exp_args:Nno \__keythms_thm_tcboxcode:nn { #1 } { \l__keythms_thm_tcbkeys_tl } } % Set up list-of definition \cs_set_eq:cN { l@ #1 } \keythms_listof_tocline: % Set default list-of display command \__keythms_listof_show_aux:n { #1 } % Set theorem style back to original state if needed \tl_if_empty:NF \l__keythms_thm_style_tl { \__keythms_theoremstyle:V \l__keythms_thm_currentthmstyle_tl } } \cs_new_protected:Npn \__keythms_thm_tcboxcode:nn #1#2 { \RequirePackage{tcolorbox} \hook_gput_code:nnn { keytheorems/#1/prehead } { keythms_tcbox } { \cs_set_eq:NN \deferred@thm@head \__keythms_thm_storedeferredthmhead:n \cs_set_eq:NN \Hy@theorem@makelinktarget \use_none:n % ^ don't like playing with hyperref internals... but don't see around % it because hyperref tries to add to para hook which doesn't work % when title set up the way we do it \cs_set_protected:Npn \thm@space@setup { \thm@preskip=0pt \thm@postskip=0pt } % ^ to match tcolorbox defaults; shouldn't interfere with user styles } \hook_gset_rule:nnnn { keytheorems/#1/posthead } { keythms_tcbox } { before } { keythms_hook_keys } \hook_gset_rule:nnnn { keytheorems/#1/prefoot } { keythms_tcbox } { after } { keythms_hook_keys } \hook_gset_rule:nnnn { keytheorems/#1/prefoot } { keythms_tcbox } { after } { keythms_qed } \tcbset { keythms_tcbox_#1/.style = { savedelimiter=#1, title={ \__keythms_thm_tcboxtemphead: }, } } \bool_if:NT \l__keythms_thm_numbered_bool { \hook_gput_code:nnn { begindocument } { . } { \IfPackageLoadedF{cleveref} { % hyperref doesn't patch \@thm if cleveref loaded \tcbset { keythms_tcbox_#1/.append~style = % fix hyperlinking { phantom={ \MakeLinkTarget*{\@currentHref} } } } } } } \hook_gput_code:nnn { keytheorems/#1/posthead } { keythms_tcbox } { \begin{tcolorbox}[keythms_tcbox_#1,#2] } \hook_gput_code:nnn { keytheorems/#1/prefoot } { keythms_tcbox } { \end{tcolorbox} } } \cs_new_protected:Npn \__keythms_thm_qedcode:nn #1#2 { \hook_gput_code:nnn { keytheorems/#1/posthead } { keythms_qed } { \exp_args:No \tl_if_novalue:nF { #2 } { \protected@edef\qedsymbol{#2} } \pushQED{\qed} } \hook_gput_code:nnn { keytheorems/#1/prefoot } { keythms_qed } { \exp_args:No \tl_if_novalue:nF { #2 } { \protected@edef\qedsymbol{#2} } \popQED } } \cs_new_eq:NN \__keythms_theoremstyle:n \theoremstyle \cs_generate_variant:Nn \__keythms_theoremstyle:n { V } % \newtheorem variants \cs_new_eq:NN \__keythms_thm_new:w \newtheorem \cs_new_protected:Npn \__keythms_thm_new_numbered:nn #1#2 { \__keythms_thm_new:w { #1 } { #2 } } \cs_generate_variant:Nn \__keythms_thm_new_numbered:nn { nV } \cs_new_protected:Npn \__keythms_thm_new_unnumbered:nn #1#2 { \__keythms_thm_new:w* { #1 } { #2 } } \cs_generate_variant:Nn \__keythms_thm_new_unnumbered:nn { nV } \cs_new_protected:Npn \__keythms_thm_new_parent:nnn #1#2#3 { \__keythms_thm_new:w { #1 } { #2 } [ #3 ] } \cs_generate_variant:Nn \__keythms_thm_new_parent:nnn { nVV } \cs_new_protected:Npn \__keythms_thm_new_sibling:nnn #1#2#3 { \__keythms_thm_new:w { #1 } [ #3 ] { #2 } } \cs_generate_variant:Nn \__keythms_thm_new_sibling:nnn { nV } \cs_new_protected:Npn \__keythms_thm_new_uuwithparent:nnn #1#2#3 { \cs_undefine:c { keythms_orig_nonumber_#1 } % for renew, declare \__keythms_thm_new_unnumbered:nn { keythms_orig_nonumber_#1 } { #2 } \__keythms_thm_new_parent:nnn { #1 } { #2 } { #3 } \DeclareEnvironmentCopy { keythms_orig_withparent_#1 } { #1 } \renewenvironment { #1 } % opt arg is implicit { \setuniqmark{ #1. \use:c {the #3} } \ifuniq{ #1. \use:c {the #3} } { \keythms_if_restating:F { \refstepcounter{ keythms_unnumbered_dummyctr } } \begin{keythms_orig_nonumber_#1} } { \begin{keythms_orig_withparent_#1} } } { \ifuniq{ #1. \use:c {the #3} } { \end{keythms_orig_nonumber_#1} } { \end{keythms_orig_withparent_#1} } } } \cs_generate_variant:Nn \__keythms_thm_new_uuwithparent:nnn { nVV } % for getting notes with continues*, use nameref if available, otherwise ltproperties \hook_gput_code:nnn { begindocument } { . } { \IfPackageLoadedTF { nameref } { \cs_new:Npn \__keythms_thmuse_recordnote: { } % nameref takes care of this \cs_new:Npn \__keythms_getrecordednote:n #1 { \getrefbykeydefault{ #1 }{ name }{ } } } { \property_new:nnnn { keytheorems/recordednote } { now } { } { \l__keythms_thmuse_note_tl } \cs_new:Npn \__keythms_getrecordednote:n #1 { \property_ref:nn { keythms_recordednote_#1 } { keytheorems/recordednote } } \cs_new:Npn \__keythms_thmuse_recordnote: { \tl_if_empty:NF \l__keythms_thmuse_note_tl { \RecordProperties { keythms_recordednote_\l__keythms_thmuse_label_tl } { keytheorems/recordednote } } } } } \keys_define:nn { keytheorems/thmuse } { label .tl_set:N = \l__keythms_thmuse_label_tl, note .tl_set:N = \l__keythms_thmuse_note_tl, note .initial:n = {}, name .meta:n = { note = #1 }, % ^ for compatibility. "name" is ambiguous and doesn't match amsthm language short-note .code:n = {}, % these do nothing at point of use short-name .code:n = {}, continues .tl_set:N = \l__keythms_thmuse_contlabel_tl, continues* .code:n = { \keys_set:nn { keytheorems/thmuse } { continues = #1 } \protected@edef \l__keythms_tmpa_tl { \__keythms_getrecordednote:n{#1} } \tl_if_empty:NF \l__keythms_tmpa_tl { \keys_set:nn { keytheorems/thmuse } { note = \l__keythms_tmpa_tl } } }, store .tl_set:N = \l__keythms_thmuse_store_tl, % should this be .tl_set_e:N ? %store .default:n = \q_no_value, % ={name} causes issues restate .meta:n = { store = #1 }, % thmtools compatibility store* .tl_set:N = \l__keythms_thmuse_storereversed_tl, restate* .meta:n = { store* = #1 }, restate-keys .clist_set:N = \l__keythms_thmuse_restatekeys_clist, listhack .choice:, % need equals sign listhack / true .code:n = \bool_set_true:N \l__keythms_thmuse_listhack_bool, listhack / false .code:n = \bool_set_false:N \l__keythms_thmuse_listhack_bool, listhack .initial:n = false, seq .code:n = {}, } \cs_new_protected:Npn \keythms_keyify_theorem:n #1 { % #1 = theorem name \DeclareEnvironmentCopy { keythms_orig_#1 } { #1 } \DeclareDocumentEnvironment { keythms_grab_#1 } { m m +b } { % ##1 = keys, ##2 = note, ##3 = theorem body \__keythms_thm_prehead_code:n { #1 } \begin{keythms_orig_#1}[{##2}] \clist_map_inline:Nn \g__keythms_restatecounters_clist { \prop_gput:Nne \g__keythms_thmuse_othercounters_prop { ####1 } { \the\value{####1} } } \__keythms_thm_posthead_code:n { #1 } % below needs to come after posthead so that correct \@currentHref % is stored for tcolorbox theorems \__keythms_thm_addcontentsdata:nnnn { #1 } { \prop_to_keyval:N \g__keythms_thmuse_othercounters_prop } { ##1 } { ##3 } \__keythms_thm_tempstorerestatedata:nnn { #1 } { ##1 } { ##3 } ##3 \__keythms_thm_prefoot_code:n { #1 } \end{keythms_orig_#1} \__keythms_thm_postfoot_code:n { #1 } } {} \DeclareDocumentEnvironment { keythms_grabreversed_#1 } { m m +b } { % ##1 = keys, ##2 = note, ##3 = theorem body \tl_if_exist:cTF { c__keythms_storeatbegin_ \l__keythms_thmuse_storereversed_tl _restatecounters_tl } { \bool_set_true:N \l__keythms_thmuse_restating_bool \exp_args:NNe \prop_set_from_keyval:Nn \l__keythms_restate_counters_prop { \tl_use:c { c__keythms_storeatbegin_ \l__keythms_thmuse_storereversed_tl _restatecounters_tl } } \prop_map_inline:Nn \l__keythms_restate_counters_prop { \tl_set:ce { l_keythms_restate_current_####1_tl } { \the\value{####1} } \setcounter { ####1 } { ####2 } % ^ FIX: what if eq's numbered by section, theorem, etc.? The % thmtools code is opaque.... Or maybe should be up to the % user to say "restate-counters={section,chapter,...}". \cs_set:cpn { theH ####1 } { \use:c { the ####1 } . \theHkeythms_restate_dummyctr } } \tl_if_empty:cTF { c__keythms_storeatbegin_ \l__keythms_thmuse_storereversed_tl _label_tl } { \refstepcounter{keythms_restate_dummyctr} } % for unnumbered theorems { \cs_set:cpn { the #1 } { \tl_use:c { c__keythms_storeatbegin_ \l__keythms_thmuse_storereversed_tl _label_tl } } \cs_set_eq:cN { c@ #1 } \c@keythms_restate_dummyctr \cs_set_eq:cN { theH #1 } \theHkeythms_restate_dummyctr % ^ why are the last two lines here? We shouldn't be referencing % restated theorems. Think it's a remnant of thmtools % WRONG: needed to make numbering correct after restated theorem. % not sure about theH. <- this is needed to prevent duplicate anchors } } { \msg_warning:nne { keytheorems } { store-reversed-not-got } { \l__keythms_thmuse_storereversed_tl } } \renewcommand\label[2][]{} % disable \label (opt arg in case cleveref loaded) \cs_set_eq:NN \ltx@label \use_none:n % disable \ltx@label \cs_set_eq:NN \property_record:nn \use_none:nn % disable \RecordProperties \cs_set_eq:NN \setuniqmark \use_none:n % work with numbered=unless-unique % QUESTION: also disable \hyper@@anchor? \MakeLinkTarget? \hook_use:n { keytheorems/#1/restated } \hook_use:n { keytheorems/allthms/restated } \__keythms_thm_prehead_code:n { #1 } \begin{keythms_orig_#1}[{##2}] \__keythms_thm_posthead_code:n { #1 } % below needs to come after posthead so that correct \@currentHref % is stored for tcolorbox theorems \__keythms_thm_addstoredreverseddata:nnn { #1 } { ##1 } { ##3 } \__keythms_thm_tempstorerestatedatareversed:nnn { #1 } { ##1 } { ##3 } ##3 \__keythms_thm_prefoot_code:n { #1 } \end{keythms_orig_#1} \__keythms_thm_postfoot_code:n { #1 } \prop_map_inline:Nn \l__keythms_restate_counters_prop { \exp_args:Nnc \setcounter { ####1 } { l_keythms_restate_current_####1_tl } } } { } % NOTE: have to do a lot of shenanigans to make sure the begin/end of grabbed % theorem env captures only the body and no package code. % This is the price of on-the-fly redefining the env to grab body \RenewDocumentEnvironment { #1 } { ={note} O{} } { \keys_set:nn { keytheorems/thmuse } { ##1 } \tl_if_empty:NF \l__keythms_thmuse_store_tl { \bool_gset_true:N \g__keythms_listof_writefile_bool \cs_set_eq:NN \__keythms_withhooks_begin:nnn \__keythms_grab_begin:nnn \cs_set_eq:NN \__keythms_withhooks_begin:nnV \__keythms_grab_begin:nnV \cs_set_eq:NN \__keythms_withhooks_end:n \__keythms_grab_end:n } \tl_if_empty:NF \l__keythms_thmuse_storereversed_tl { \bool_gset_true:N \g__keythms_listof_writefile_bool \cs_set_eq:NN \__keythms_withhooks_begin:nnn \__keythms_grabreversed_begin:nnn \cs_set_eq:NN \__keythms_withhooks_begin:nnV \__keythms_grabreversed_begin:nnV \cs_set_eq:NN \__keythms_withhooks_end:n \__keythms_grabreversed_end:n } \__keythms_thm_prehead_continues_code:n { #1 } \__keythms_withhooks_begin:nnV { #1 } { ##1 } \l__keythms_thmuse_note_tl } { \__keythms_withhooks_end:n { #1 } \tl_if_empty:NF \l__keythms_thmuse_store_tl { \cs_if_exist:cF { __keythms_getthm_ \l__keythms_thmuse_store_tl _theorem } { \cs_new:cpe { __keythms_getthm_ \l__keythms_thmuse_store_tl _theorem } { \exp_not:N \__keythms_getthm_theorem:nnnnn \exp_not:o { \g__keythms_thmuse_temprestatedata_tl } } \cs_new:cpe { __keythms_getthm_ \l__keythms_thmuse_store_tl _body } { \exp_not:N \__keythms_getthm_body:nn \exp_args:No \exp_not:o { \exp_after:wN \__keythms_use_iii_v_braced:nnnnn \g__keythms_thmuse_temprestatedata_tl } } } } \tl_if_empty:NF \l__keythms_thmuse_storereversed_tl { \cs_if_exist:cF { __keythms_getthm_ \l__keythms_thmuse_storereversed_tl _theorem } { \cs_new:cpe { __keythms_getthm_ \l__keythms_thmuse_storereversed_tl _theorem } { \exp_not:N \__keythms_getthmreversed_theorem:nnn \exp_not:o { \g__keythms_thmuse_temprestatedatareversed_tl } } \cs_new:cpn { __keythms_getthm_ \l__keythms_thmuse_storereversed_tl _body } { \textbf{??} \msg_warning:nnn { keytheorems } { restate-body-never-got } { #1 } } } } } } \cs_new:Npn \__keythms_use_iii_v_braced:nnnnn #1#2#3#4#5 { {#3}{#5} } \cs_new_protected:Npn \__keythms_withhooks_begin:nnn #1#2#3 { % #1 = theorem name, #2 = keys, #3 = note \__keythms_thm_prehead_code:n { #1 } \begin{keythms_orig_#1}[{#3}] \__keythms_thm_posthead_code:n { #1 } \__keythms_thm_addcontentsdata:nnnn { #1 } { } { #2 } { } \ignorespaces % I hope this is alright } \cs_generate_variant:Nn \__keythms_withhooks_begin:nnn { nnV } \cs_new_protected:Npn \__keythms_withhooks_end:n #1 { \__keythms_thm_prefoot_code:n { #1 } \end{keythms_orig_#1} \__keythms_thm_postfoot_code:n { #1 } } \cs_new_protected:Npn \__keythms_grab_begin:nnn #1#2#3 { % #1 = theorem name, #2 = keys, #3 = note \begin{keythms_grab_#1}{#2}{#3} } \cs_generate_variant:Nn \__keythms_grab_begin:nnn { nnV } \cs_new_protected:Npn \__keythms_grab_end:n #1 { \end{keythms_grab_#1} } \cs_new_protected:Npn \__keythms_grabreversed_begin:nnn #1#2#3 { % #1 = theorem name, #2 = keys, #3 = note \begin{keythms_grabreversed_#1}{#2}{#3} } \cs_generate_variant:Nn \__keythms_grabreversed_begin:nnn { nnV } \cs_new_protected:Npn \__keythms_grabreversed_end:n #1 { \end{keythms_grabreversed_#1} } \cs_new_protected:Npn \__keythms_orig_begin:nn #1#2 { \begin{keythms_orig_#1}[{#2}] } \cs_generate_variant:Nn \__keythms_orig_begin:nn { nV } \cs_new_protected:Npn \__keythms_orig_end:n #1 { \end{keythms_orig_#1} } \cs_new_protected:Npn \__keythms_thm_prehead_code:n #1 { % #1 = theorem name \tl_set:Nn \l_keythms_thmuse_envname_tl { #1 } \hook_use:n { keytheorems/#1/prehead } \hook_use:n { keytheorems/allthms/prehead } } %% this below has to be separate from prehead_code above since we need to add %% continues-code to note before retrieving it in \__keythms_withhooks_begin:nnV \cs_new_protected:Npn \__keythms_thm_prehead_continues_code:n #1 { % #1 = theorem name \tl_if_empty:NF \l__keythms_thmuse_contlabel_tl { \tl_if_empty:NF \l__keythms_thmuse_note_tl { \tl_put_right:Nn \l__keythms_thmuse_note_tl { , ~ } } \tl_put_right:Ne \l__keythms_thmuse_note_tl { \__keythms_thmuse_continues:V \l__keythms_thmuse_contlabel_tl } \cs_set:cpn { the #1 } { \getrefnumber { \l__keythms_thmuse_contlabel_tl } } \cs_set_eq:cN { c@ #1 } \c@keythms_continues_dummyctr \cs_set_eq:cN { theH #1 } \theHkeythms_continues_dummyctr %\cs_set_eq:NN \setuniqmark \use_none:n % not the right fix } } \cs_new_protected:Npn \__keythms_thm_posthead_code:n #1 { % #1 = theorem name \hook_use:n { keytheorems/#1/posthead } \hook_use:n { keytheorems/allthms/posthead } \tl_if_empty:NF \l__keythms_thmuse_label_tl { \label{ \l__keythms_thmuse_label_tl } \__keythms_thmuse_recordnote: } \bool_if:NT \l__keythms_thmuse_listhack_bool { % straight from thm-amsthm.sty \leavevmode \vspace{-\baselineskip}% \par \everypar{\setbox\z@\lastbox\everypar{}}% } } \cs_new_protected:Npn \__keythms_thm_prefoot_code:n #1 { % #1 = theorem name \hook_use:n { keytheorems/allthms/prefoot } \hook_use:n { keytheorems/#1/prefoot } } \cs_new_protected:Npn \__keythms_thm_postfoot_code:n #1 { % #1 = theorem name \hook_use:n { keytheorems/allthms/postfoot } \hook_use:n { keytheorems/#1/postfoot } } \cs_new_protected:Npn \__keythms_thm_addcontentsdata:nnnn #1#2#3#4 { % #1 = theorem name, #2 = stored counters, #3 = keys, #4 = body \keythms_listof_chaptervspacehack: \iow_shipout:Ne \@auxout { \exp_not:N \@writefile { thlist } { \KeyThmsSavedTheorem{ #1 } { \@currentlabel } { \@currentHref } { \thepage } { #2 } { \exp_not:n { #3 } } % do we want any expansion here, perhaps { \exp_not:n { #4 } } % with \text_expand:n ? } } } \cs_new_protected:Npn \__keythms_thm_addstoredreverseddata:nnn #1#2#3 { \iow_shipout:Ne \@auxout { \exp_not:N \@writefile { thlist } { \KeyThmsSavedTheoremReversed { \l__keythms_thmuse_storereversed_tl } { #1 } { \exp_not:n { #2 } } { \exp_not:n { #3 } } } } } \cs_new_protected:Npn \__keythms_thm_tempstorerestatedata:nnn #1#2#3 { % #1 = theorem name, #2 = keys, #3 = body \tl_gset:Ne \g__keythms_thmuse_temprestatedata_tl % needs to be global to get out of env { { #1 } { \@currentlabel } { \prop_to_keyval:N \g__keythms_thmuse_othercounters_prop } { \exp_not:n { #2 } } % do we want any expansion here, perhaps { \exp_not:n { #3 } } % with \text_expand:n ? } } \cs_new_protected:Npn \__keythms_thm_tempstorerestatedatareversed:nnn #1#2#3 { \tl_gset:Ne \g__keythms_thmuse_temprestatedatareversed_tl % needs to be global to get out of env { { #1 } { \exp_not:n { #2 } } { \exp_not:n { #3 } } } } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Retrieving Theorem Data %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \cs_new_protected:Npn \KeyThmsSavedTheorem #1#2#3#4#5#6#7 % 7th arg is body { \use:c { __keythms_thmitem_#1:nnnnnn } {#2}{#3}{#4}{#5}{#6}{#7} } \cs_new_protected:Npn \KeyThmsSavedTheoremReversed #1#2#3#4 { } % gobble by default \keys_define:nn { keytheorems/storeatbegin } { store .tl_set:N = \l__keythms_storeatbegin_store_tl, restate .meta:n = { store=#1 }, store* .tl_set:N = \l__keythms_storeatbegin_storereversed_tl, restate* .meta:n = { store*=#1 }, unknown .code:n = { } % do nothing with unknown keys } \cs_new_protected:Npn \KeyThmsContentsLine #1 { #1 } \NewDocumentCommand \addtheoremcontentsline { m m } { \addtocontents { thlist } { \KeyThmsContentsLine { % copied from def of \addcontentsline \protect\contentsline{#1}{#2}{\thepage}{} \protected@file@percent } } } \NewDocumentCommand \addtotheoremcontents { m } { \addtocontents { thlist } { \KeyThmsContentsLine { #1 } } } \hook_gput_code:nnn { begindocument } { . } { \group_begin: \cs_set_eq:NN \KeyThmsContentsLine \use_none:n \cs_set_eq:NN \KeyThmsAddvspace \use_none:n \cs_set_protected:Npn \KeyThmsSavedTheorem #1#2#3#4#5#6#7 { \group_begin: \keys_set:nn { keytheorems/storeatbegin } { #6 } \tl_if_empty:NF \l__keythms_storeatbegin_store_tl { \cs_new_protected:cpn { __keythms_getthm_ \l__keythms_storeatbegin_store_tl _theorem } { \__keythms_getthm_theorem:nnnnn {#1}{#2}{#5}{#6}{#7} } \cs_new_protected:cpn { __keythms_getthm_ \l__keythms_storeatbegin_store_tl _body } { \__keythms_getthm_body:nn {#5}{#7} } } \tl_if_empty:NF \l__keythms_storeatbegin_storereversed_tl { \tl_const:cn { c__keythms_storeatbegin_ \l__keythms_storeatbegin_storereversed_tl _label_tl } { #2 } \tl_const:cn { c__keythms_storeatbegin_ \l__keythms_storeatbegin_storereversed_tl _restatecounters_tl } { #5 } \cs_new_protected:cpn { __keythms_getthm_ \l__keythms_storeatbegin_storereversed_tl _body } { \__keythms_getthm_body:nn {#5}{#7} } } \group_end: } \cs_set_protected:Npn \KeyThmsSavedTheoremReversed #1#2#3#4 { \bool_new:c { g__keythms_restate_#1_called_bool } \cs_gset:cpn { __keythms_getthm_ #1 _theorem } { \__keythms_getthmreversed_theorem:nnn { #2 } { #3 } { #4 } } \hook_gput_code:nnn { begindocument/end } { . } { \cs_if_exist:cF { __keythms_getthm_ #1 _body } { \cs_new_protected:cpn { __keythms_getthm_ #1 _body } { \textbf{??} \msg_warning:nnn { keytheorems } { restate-body-never-got } { #1 } } } } } \file_if_exist_input:n { \c_sys_jobname_str.thlist } \group_end: } \prg_new_conditional:Npnn \keythms_if_restating: { T, F, TF } { \bool_if:NTF \l__keythms_thmuse_restating_bool { \prg_return_true: } { \prg_return_false: } } \NewDocumentCommand \IfRestatingTF { } { \keythms_if_restating:TF } \NewDocumentCommand \IfRestatingT { } { \keythms_if_restating:T } \NewDocumentCommand \IfRestatingF { } { \keythms_if_restating:F } \cs_new_protected:Npn \__keythms_getthm_theorem:nnnnn #1#2#3#4#5 { % #1 = name, #2 = number, #3 = restate counters, #4 = keys, #5 = theorem body \group_begin: \bool_set_true:N \l__keythms_thmuse_restating_bool \prop_set_from_keyval:Nn \l__keythms_restate_counters_prop { #3 } \prop_map_inline:Nn \l__keythms_restate_counters_prop { \tl_set:ce { l_keythms_restate_current_##1_tl } { \the\value{##1} } \setcounter { ##1 } { ##2 } % ^ FIX: what if eq's numbered by section, theorem, etc.? The % thmtools code is opaque.... Or maybe should be up to the % user to say "restate-counters={section,chapter,...}". \cs_set:cpn { theH ##1 } { \use:c { the ##1 } . \theHkeythms_restate_dummyctr } } \tl_if_empty:nTF { #2 } { \refstepcounter{keythms_restate_dummyctr} } % for unnumbered theorems { \cs_set:cpn { the #1 } { #2 } \cs_set_eq:cN { c@ #1 } \c@keythms_restate_dummyctr \cs_set_eq:cN { theH #1 } \theHkeythms_restate_dummyctr % ^ why are the last two lines here? We shouldn't be referencing % restated theorems. Think it's a remnant of thmtools % WRONG: needed to make numbering correct after restated theorem. % not sure about theH. <- this is needed to prevent duplicate anchors } \renewcommand\label[2][]{} % disable \label (opt arg in case cleveref loaded) \cs_set_eq:NN \ltx@label \use_none:n % disable \ltx@label \cs_set_eq:NN \property_record:nn \use_none:nn % disable \RecordProperties \cs_set_eq:NN \setuniqmark \use_none:n % work with numbered=unless-unique % QUESTION: also disable \hyper@@anchor? \MakeLinkTarget? \keys_set:nn { keytheorems/thmuse } { #4 } \keys_set:no { keytheorems/thmuse } { \l__keythms_thmuse_restatekeys_clist } \hook_use:n { keytheorems/#1/restated } \hook_use:n { keytheorems/allthms/restated } \__keythms_thm_prehead_continues_code:n { #1 } \__keythms_thm_prehead_code:n { #1 } \__keythms_orig_begin:nV { #1 } \l__keythms_thmuse_note_tl \__keythms_thm_posthead_code:n { #1 } #5 \__keythms_thm_prefoot_code:n { #1 } \__keythms_orig_end:n { #1 } \__keythms_thm_postfoot_code:n { #1 } \prop_map_inline:Nn \l__keythms_restate_counters_prop { \exp_args:Nnc \setcounter { ##1 } { l_keythms_restate_current_##1_tl } } \group_end: } \cs_new_protected:Npn \__keythms_getthm_body:nn #1#2 { % #1 = restate counters, #2 = theorem body \group_begin: \bool_set_true:N \l__keythms_thmuse_restating_bool \prop_set_from_keyval:Nn \l__keythms_restate_counters_prop { #1 } \prop_map_inline:Nn \l__keythms_restate_counters_prop { \tl_set:ce { l_keythms_restate_current_##1_tl } { \the\value{##1} } \setcounter { ##1 } { ##2 } % ^ FIX: what if eq's numbered by section, theorem, etc.? The % thmtools code is opaque.... Or maybe should be up to the % user to say "restate-counters={section,chapter,...}". \cs_set:cpn { theH ##1 } { \use:c { the ##1 } . \theHkeythms_restate_dummyctr } } \refstepcounter{keythms_restate_dummyctr} \renewcommand\label[2][]{} % disable \label (opt arg in case cleveref loaded) \cs_set_eq:NN \ltx@label \use_none:n % disable \ltx@label \cs_set_eq:NN \property_record:nn \use_none:nn % disable \RecordProperties \hook_use:n { keytheorems/#1/restated } \hook_use:n { keytheorems/allthms/restated } #2 \prop_map_inline:Nn \l__keythms_restate_counters_prop { \exp_args:Nnc \setcounter { ##1 } { l_keythms_restate_current_##1_tl } } \group_end: } \cs_new_protected:Npn \__keythms_getthmreversed_theorem:nnn #1#2#3 { % #1 = envname, #2 = keys, #3 = theorem body \group_begin: \bool_set_false:N \l__keythms_thmuse_restating_bool \keys_set:nn { keytheorems/thmuse } { #2 } \keys_set:no { keytheorems/thmuse } { \l__keythms_thmuse_restatekeys_clist } \__keythms_thm_prehead_continues_code:n { #1 } \__keythms_thm_prehead_code:n { #1 } \__keythms_orig_begin:nV { #1 } \l__keythms_thmuse_note_tl \clist_map_inline:Nn \g__keythms_restatecounters_clist { \prop_gput:Nne \g__keythms_thmuse_othercounters_prop { ##1 } { \the\value{##1} } } \__keythms_thm_posthead_code:n { #1 } \__keythms_thm_addcontentsdata:nnnn { #1 } { \prop_to_keyval:N \g__keythms_thmuse_othercounters_prop } { #2 } { #3 } \__keythms_thm_tempstorerestatedata:nnn { #1 } { #2 } { #3 } % for defining getthm_body #3 \__keythms_thm_prefoot_code:n { #1 } \__keythms_orig_end:n { #1 } \__keythms_thm_postfoot_code:n { #1 } % While we're here, define getthm_body \cs_if_exist:cF { __keythms_getthm_ \l__keythms_thmuse_storereversed_tl _body } { \cs_new:cpe { __keythms_getthm_ \l__keythms_thmuse_storereversed_tl _body } { \exp_not:N \__keythms_getthm_body:nn \exp_args:No \exp_not:o { \exp_after:wN \__keythms_use_iii_v_braced:nnnnn \g__keythms_thmuse_temprestatedata_tl } } } \bool_gset_true:c { g__keythms_restate_ \l__keythms_thmuse_storereversed_tl _called_bool } \group_end: } % \getkeytheorem[]{} \NewDocumentCommand \getkeytheorem { O{theorem} m } { \cs_if_exist:cTF { __keythms_getthm_#2_#1 } { \bool_if_exist:cTF { g__keythms_restate_#2_called_bool } { \str_if_eq:nnTF { #1 } { theorem } { \bool_if:cTF { g__keythms_restate_#2_called_bool } { \msg_error:nnn { keytheorems } { store-reversed-called-twice } { #2 } } { \use:c { __keythms_getthm_#2_#1 } } } { \use:c { __keythms_getthm_#2_#1 } } } { \use:c { __keythms_getthm_#2_#1 } } } { \textbf{??} \cs_if_exist:cTF { __keythms_getthm_#2_theorem } { \msg_warning:nnnn { keytheorems } { no-prop-for-stored-theorem } { #1 } { #2 } % should this be an error? } { \msg_warning:nnn { keytheorems } { no-stored-theorem } { #2 } } } } %%%%%%%%%%%%%%%%%%%%% %%% Theorem Hooks %%% %%%%%%%%%%%%%%%%%%%%% %%% \addtotheoremhook[]{}{} \NewDocumentCommand \addtotheoremhook { O{allthms} m +m } { \__hook_if_declared:nTF { keytheorems/allthms/#2 } { \hook_gput_code:nnn { keytheorems/#1/#2 } { . } { #3 } } { \msg_error:nnn { keytheorems } { undefined-thm-hook } { #2 } } } % NOTE: I think it's OK we use the internal \__hook_if_declared:nTF above % since we don't need to worry about the user creating new theorem hooks % so, as we're only checking the existence of hooks created by us, it's OK. %%%%%%%%%%%%%%%%%%%%%%%% %%% List of Theorems %%% %%%%%%%%%%%%%%%%%%%%%%%% \keys_define:nn { keytheorems/listof } { numwidth .dim_set:N = \l__keythms_listof_numwidth_dim, numwidth .initial:n = 2.3em, indent .dim_set:N = \l__keythms_listof_indent_dim, indent .initial:n = 1.5em, ignore .code:n = { \hook_gput_code:nnn { begindocument/before } { keytheorems } { \keythms_listof_ignore:n { #1 } } }, show .code:n = { \hook_gput_code:nnn { begindocument/before } { keytheorems } { \keythms_listof_show:n { #1 } } }, onlynamed .code:n = { \hook_gput_code:nnn { begindocument/before } { keytheorems } { \keythms_listof_onlynamed:n { #1 } } }, onlynamed .default:n = \q_no_value, onlynumbered .code:n = { \hook_gput_code:nnn { begindocument/before } { keytheorems } { \keythms_listof_onlynumbered:n { #1 } } }, onlynumbered .default:n = \q_no_value, ignoreall .code:n = { \hook_gput_code:nnn { begindocument/before } { keytheorems } % in case called before theorem defined { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_ignore_aux:n { ##1 } } } }, showall .code:n = { \hook_gput_code:nnn { begindocument/before } { keytheorems } % in case called before theorem defined { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_show_aux:n { ##1 } } } }, title .tl_set:N = \l__keythms_listof_title_tl, title .initial:n = \GetTranslation{keythms_listof_title}, swapnumber .bool_set:N = \l__keythms_listof_swapnumber_bool, swapnumber .initial:n = false, title-code .cs_set:Np = \__keythms_listof_titlecmd:n #1, no-title .bool_set:N = \l__keythms_listof_notitle_bool, no-title .initial:n = false, print-body .code:n = { \cs_set_protected:Nn \keythms_listof_listcmd:nnnnnnn { \tl_if_empty:nF { ##7 } { \__keythms_getthm_theorem:nnnnn {##1}{##2}{##5}{##6}{##7} } } \cs_set_eq:NN \KeyThmsContentsLine \use_none:n % ^ I assume we want this? \cs_set_eq:NN \KeyThmsAddvspace \use_none:n }, note-code .cs_set:Np = \__keythms_listof_notecmd:n #1, note-code .initial:n = { ~ (#1) }, no-continues .bool_set:N = \l__keythms_listof_nocont_bool, no-continues .initial:n = false, no-chapter-skip .bool_set:N = \l__keythms_listof_nochapskip_bool, no-chapter-skip .initial:n = false, chapter-skip-length .dim_set:N = \keythms@listof@chaptervspace@dim, chapter-skip-length .initial:n = 10pt, no-toc .bool_set:N = \l__keythms_listof_notoc_bool, no-toc .initial:n = false, % false does not mean "to-toc" for standard classes } \hook_gput_code:nnn { begindocument } { . } % redefine these keys at begindocument { \keys_define:nn { keytheorems/listof } { ignore .code:n = \keythms_listof_ignore:n { #1 }, show .code:n = \keythms_listof_show:n { #1 }, onlynamed .code:n = \keythms_listof_onlynamed:n { #1 }, onlynamed .default:n = \q_no_value, onlynumbered .code:n = \keythms_listof_onlynumbered:n { #1 }, onlynumbered .default:n = \q_no_value, ignoreall .code:n = { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_ignore_aux:n { ##1 } } }, showall .code:n = { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_show_aux:n { ##1 } } }, seq .code:n = \keythms_listof_showseq:n { #1 }, } } \NewDocumentCommand \keytheoremlistset { m } { \keys_set:nn { keytheorems/listof } { #1 } } \cs_new_protected:Npn \keythms_listof_ignore:n #1 { \clist_map_inline:nn { #1 } { \__keythms_listof_ignore_aux:n { ##1 } } } \cs_new_protected:Npn \__keythms_listof_ignore_aux:n #1 { \cs_set_protected:cpn { __keythms_thmitem_#1:nnnnnn } ##1##2##3##4##5##6 { } } \cs_new_protected:Npn \keythms_listof_show:n #1 { \clist_map_inline:nn { #1 } { \__keythms_listof_show_aux:n { ##1 } } } \cs_new_protected:Npn \__keythms_listof_show_aux:n #1 { \cs_set_protected:cpn { __keythms_thmitem_#1:nnnnnn } ##1##2##3##4##5##6 { \__keythms_listof_listcmd_setup:nn { ##5 } { \keythms_listof_listcmd:nnnnnnn {#1}{##1}{##2}{##3}{##4}{##5}{##6} } } } \cs_new_protected:Npn \keythms_listof_onlynamed:n #1 { \quark_if_no_value:nTF { #1 } { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_onlynamed_aux:n { ##1 } } } { \clist_map_inline:nn { #1 } { \__keythms_listof_onlynamed_aux:n { ##1 } } } } \cs_new_protected:Npn \__keythms_listof_onlynamed_aux:n #1 { \cs_set_protected:cpn { __keythms_thmitem_#1:nnnnnn } ##1##2##3##4##5##6 { \__keythms_listof_listcmd_setup:nn { ##5 } { \tl_if_empty:NF \l__keythms_listofheading_note_tl { \keythms_listof_listcmd:nnnnnnn {#1}{##1}{##2}{##3}{##4}{##5}{##6} } } } } \cs_new_protected:Npn \keythms_listof_onlynumbered:n #1 { \quark_if_no_value:nTF { #1 } { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_onlynumbered_aux:n { ##1 } } } { \clist_map_inline:nn { #1 } { \__keythms_listof_onlynumbered_aux:n { ##1 } } } } \cs_new_protected:Npn \__keythms_listof_onlynumbered_aux:n #1 { \cs_set_protected:cpn { __keythms_thmitem_#1:nnnnnn } ##1##2##3##4##5##6 { \__keythms_listof_listcmd_setup:nn { ##5 } { \tl_if_empty:nF { ##1 } { \keythms_listof_listcmd:nnnnnnn {#1}{##1}{##2}{##3}{##4}{##5}{##6} } } } } \cs_new_protected:Npn \keythms_listof_showseq:n #1 { \prop_map_inline:Nn \g__keythms_thmnames_prop { \__keythms_listof_showseq_aux:nn { #1 } { ##1 } } } \cs_new_protected:Npn \__keythms_listof_showseq_aux:nn #1#2 { % #1 = seq name, #2 = theorem name \cs_set_protected:cpn { __keythms_thmitem_#2:nnnnnn } ##1##2##3##4##5##6 { \__keythms_listof_listcmd_setup:nn { ##5 } { \tl_if_eq:NnT \l__keythms_listofheading_seq_tl { #1 } { \keythms_listof_listcmd:nnnnnnn {#2}{##1}{##2}{##3}{##4}{##5}{##6} } } } } % Seems unnecessary to repeat all this for reading the keyvals from seq. % In thmtools they just hook the "thmitem" definition into the theorem declaration. %% NOTE ON ABOVE: this gives more flexibility to define different kinds of lists. %% See acro.sty for template idea. \keys_define:nn { keytheorems/listofheading } { note .tl_set:N = \l__keythms_listofheading_note_tl, name .meta:n = { note = #1 }, short-note .tl_set:N = \l__keythms_listofheading_shortnote_tl, short-name .meta:n = { short-note = #1 }, continues .tl_set:N = \l__keythms_listofheading_contlabel_tl, continues* .code:n = { \keys_set:nn { keytheorems/listofheading } { continues = #1 } \protected@edef \l__keythms_tmpa_tl { \__keythms_getrecordednote:n{#1} } \tl_if_empty:NF \l__keythms_tmpa_tl { \keys_set:nn { keytheorems/listofheading } { note = \l__keythms_tmpa_tl } } }, seq .tl_set:N = \l__keythms_listofheading_seq_tl, store* .code:n = \bool_set_true:N \l__keythms_listofheading_storereversed_bool, restate* .meta:n = { store*=#1 }, restate-keys .clist_set:N = \l__keythms_listofheading_restatekeys_clist, unknown .code:n = { } % do nothing with unknown keys % ^ this is OK because we have total control over possible keys; if invalid % key is given to theorem then an error will be raised there } \cs_new:Npn \__keythms_listof_printheading: { \tl_if_empty:NTF \l__keythms_listofheading_shortnote_tl { \tl_if_empty:NF \l__keythms_listofheading_note_tl { \__keythms_listof_notecmd:n { \l__keythms_listofheading_note_tl } } } { \__keythms_listof_notecmd:n { \l__keythms_listofheading_shortnote_tl } } } \cs_new:Npn \__keythms_listof_default_listcmd:nnnnnnn #1#2#3#4#5#6#7 { \contentsline{ #1 } { \bool_if:NTF \l__keythms_listof_swapnumber_bool { \prop_item:Nn \g__keythms_thmnames_prop { #1 } ~ #2 } { \numberline{ #2 } \prop_item:Nn \g__keythms_thmnames_prop { #1 } } \__keythms_listof_printheading: } { #4 }{ #3 } } % NOTE: We still need to do this setup for [print-body] so that onlynamed works \cs_new_protected:Npn \__keythms_listof_listcmd_setup:nn #1#2 { % #1 = keys, #2 = list command \group_begin: \keys_set:nn { keytheorems/listofheading } { #1 } \bool_if:NT \l__keythms_listofheading_storereversed_bool { \keys_set:no { keytheorems/listofheading } { \l__keythms_listofheading_restatekeys_clist } } \tl_if_empty:NTF \l__keythms_listofheading_contlabel_tl { #2 } { \bool_if:NF \l__keythms_listof_nocont_bool { \tl_if_empty:NF \l__keythms_listofheading_note_tl { \tl_put_right:Nn \l__keythms_listofheading_note_tl { , ~ } } \tl_put_right:Nn \l__keythms_listofheading_note_tl { \__keythms_thmuse_continues:V \l__keythms_listofheading_contlabel_tl } #2 } } \group_end: } % set default listcmd \cs_new_eq:NN \keythms_listof_listcmd:nnnnnnn \__keythms_listof_default_listcmd:nnnnnnn \cs_if_exist:NTF \chapter { \cs_set_protected:Npn \__keythms_listof_titlecmd:n #1 { \chapter*{#1} } } { \cs_set_protected:Npn \__keythms_listof_titlecmd:n #1 { \section*{#1} } } \cs_new_protected:Npn \__keythms_listof_default_tocline: { \@dottedtocline{ 1 }{ \l__keythms_listof_indent_dim } { \l__keythms_listof_numwidth_dim } } \cs_new_eq:NN \keythms_listof_tocline: \__keythms_listof_default_tocline: \NewDocumentCommand \listofkeytheorems { O{} } { \bool_gset_true:N \g__keythms_listof_writefile_bool \group_begin: \keys_set:nn { keytheorems/listof } { #1 } \bool_if:NT \l__keythms_listof_nochapskip_bool { \cs_set_eq:NN \KeyThmsAddvspace \use_none:n } \bool_if:NF \l__keythms_listof_notitle_bool { \__keythms_listof_titlecmd:n { \l__keythms_listof_title_tl } \@mkboth % QUESTION: should this go in titlecmd ? { \MakeUppercase \l__keythms_listof_title_tl } { \MakeUppercase \l__keythms_listof_title_tl } } \legacy_if_set_false:n { @filesw } \@starttoc{ thlist } \group_end: } % ^ unlike thmtools we don't use the class's style of \listoffigures because % we want control over title-code, no-title, etc. But this means we have to guess % things like marks, sectioning command, etc. \hook_gput_code:nnn { enddocument } { . } { \bool_if:NTF \g__keythms_listof_writefile_bool { \legacy_if:nT { @filesw } { \iow_new:N \tf@thlist \iow_open:Nn \tf@thlist { \c_sys_jobname_str.thlist } } } { % if .thlist file left over from previous run but not needed, clear it \file_if_exist:nT { \c_sys_jobname_str.thlist } { \iow_open:Nn \g_tmpa_iow { \c_sys_jobname_str.thlist } \iow_close:N \g_tmpa_iow } } } % chaptervspacehack (code translated from thmtools) \cs_new_eq:NN \KeyThmsAddvspace \addvspace \int_new:N \g_keythms_listof_prevchapter_int \int_gset:Nn \g_keythms_listof_prevchapter_int { 1 } % ^ if this is zero, bad things happen if title-code is changed; anyways don't % need addvspace at top \cs_new_protected:Npn \keythms_listof_chaptervspacehack: { } \cs_if_exist:cT { c@chapter } { \cs_if_eq:NNF \c@chapter \relax { \cs_set_protected:Npn \keythms_listof_chaptervspacehack: { \int_compare:nNnF { \value{chapter} } = { \g_keythms_listof_prevchapter_int } { \addtocontents{ thlist } { \protect\KeyThmsAddvspace { \keythms@listof@chaptervspace@dim } } \int_gset:Nn \g_keythms_listof_prevchapter_int { \value{chapter} } } } } } %%%%%%%%%%%%%%%% %%% \Autoref %%% %%%%%%%%%%%%%%%% \ProvideDocumentCommand { \Autoref } { s m } { \IfPackageLoadedTF { hyperref } { \group_begin: \cs_set_eq:NN \HyRef@testreftype \__keythms_Autoref_testreftype:w \IfBooleanTF { #1 } { \autoref*{#2} } { \autoref{#2} } \group_end: } { \msg_error:nn { keytheorems } { hyperref-Autoref } } } \cs_new_protected:Npn \__keythms_Autoref_testreftype:w #1.#2\\ { \cs_if_exist:cTF { #1 Autorefname } { \cs_set:Npe \HyRef@currentHtag { \exp_not:N \use:c { #1 Autorefname } \exp_not:N \c_space_token } } { \msg_warning:nnn { keytheorems } { no-Autorefname } { #1 } } } %%%%%%%%%%%%%%%%%%% %%% Global Keys %%% %%%%%%%%%%%%%%%%%%% \keys_define:nn { keytheorems } { restate-counters .code:n = { \clist_map_inline:nn { #1 } { \tl_new:c { l_keythms_restate_current_##1_tl } } \clist_gput_right:Nn \g__keythms_restatecounters_clist { #1 } }, restate-counters .initial:n = equation, continues-code .cs_set:Np = \__keythms_thmuse_continues:n #1, continues-code .initial:n = { % not sure how best to handle this translation \GetTranslation{keythms_continues}\pageref{#1} }, qed-symbol .cs_set_protected:Np = \qedsymbol, overload .code:n = \__keythms_overload_code:, overload .value_forbidden:n = true, overload .usage:n = preamble, thmtools-compat .code:n = { % prevent loading the code again if key called twice \bool_if:NF \g__keythms_thmtoolscompat_bool { \__keythms_thmtoolscompat_code: } }, thmtools-compat .value_forbidden:n = true, thmtools-compat .usage:n = preamble, store-all .code:n = \__keythms_storeall_code:, store-all .value_forbidden:n = true, store-all .usage:n = preamble, auto-translate .bool_gset:N = \g__keythms_autotranslate_bool, auto-translate .initial:n = true, store-sets-label .code:n = { \keys_define:nn { keytheorems/thmuse } { store .code:n = { \tl_set:Nn \l__keythms_thmuse_label_tl { ##1 } \tl_set:Nn \l__keythms_thmuse_store_tl { ##1 } }, store* .code:n = { \tl_set:Nn \l__keythms_thmuse_label_tl { ##1 } \tl_set:Nn \l__keythms_thmuse_storereversed_tl { ##1 } }, } }, } \cs_generate_variant:Nn \__keythms_thmuse_continues:n { V } % \keytheoremset{} \NewDocumentCommand \keytheoremset { m } { \keys_set:nn { keytheorems } { #1 } } \cs_new_protected:Npn \__keythms_overload_code: { \RenewDocumentCommand { \newtheorem } { s m o m o } { \IfBooleanTF { ##1 } { \keythms_thm_newkeythm:nn { ##2 } { name=##4, numbered=no } } { \IfNoValueTF { ##3 } { \IfNoValueTF { ##5 } { \keythms_thm_newkeythm:nn { ##2 } { name=##4 } } { \keythms_thm_newkeythm:nn { ##2 } { name=##4, parent=##5 } } } { \keythms_thm_newkeythm:nn { ##2 } { name=##4, sibling=##3 } } } } } \cs_new_protected:Npn \__keythms_thmtoolscompat_code: { \bool_gset_true:N \g__keythms_thmtoolscompat_bool \__keythms_overload_code: % since thmtools overwrites \newtheorem \ProvideDocumentCommand { \declaretheoremstyle } { O{} m } { \declarekeytheoremstyle { ##2 } { ##1 } } \ProvideDocumentCommand { \declaretheorem } { O{} m } { \newkeytheorem { ##2 } [ ##1 ] } \ProvideDocumentEnvironment { restatable } { O{} m m } { % need to redefine this to add store to thlist since we set it outside env \cs_set_protected:Npn \__keythms_thm_addcontentsdata:nnnn ####1####2####3####4 { % #1 = theorem name, #2 = stored counters, #3 = keys, #4 = body \keythms_listof_chaptervspacehack: \iow_shipout:Ne \@auxout { \exp_not:N \@writefile { thlist } { \KeyThmsSavedTheorem{ ####1 } { \@currentlabel } { \@currentHref } { \thepage } { ####2 } { store=\l__keythms_thmuse_store_tl,\exp_not:n { ####3 } } % this line is changed { \exp_not:n { ####4 } } } } } % set store outside [] so keyless note is recognized \keys_set:nn { keytheorems/thmuse } { store=##3 } \begin{##2}[##1] } { \end{##2} \cs_new_protected:cpn { ##3 } { % make \foo and \foo* identical \peek_meaning_remove:NTF * { \getkeytheorem{ ##3 } } { \getkeytheorem{ ##3 } } } } \ProvideDocumentEnvironment { restatable* } { O{} m m } { % need to redefine this to add store* to thlist since we set it outside env \cs_set_protected:Npn \__keythms_thm_addstoredreverseddata:nnn ####1####2####3 { \iow_shipout:Ne \@auxout { \exp_not:N \@writefile { thlist } { \KeyThmsSavedTheoremReversed { \l__keythms_thmuse_storereversed_tl } { ####1 } { store*=\l__keythms_thmuse_storereversed_tl,\exp_not:n { ####2 } } % this line is changed { \exp_not:n { ####3 } } } } } % set store* outside [] so keyless note is recognized \keys_set:nn { keytheorems/thmuse } { store*=##3 } \begin{##2}[##1] } { \end{##2} \cs_new_protected:cpn { ##3 } { % make \foo and \foo* identical \peek_meaning_remove:NTF * { \getkeytheorem{ ##3 } } { \getkeytheorem{ ##3 } } } } \ProvideDocumentCommand { \listoftheorems } { } { \listofkeytheorems } \ProvideDocumentCommand { \addtotheorempreheadhook } { O{allthms} m } { \addtotheoremhook [ ##1 ] { prehead } { ##2 } } \ProvideDocumentCommand { \addtotheorempostheadhook } { O{allthms} m } { \addtotheoremhook [ ##1 ] { posthead } { ##2 } } \ProvideDocumentCommand { \addtotheoremprefoothook } { O{allthms} m } { \addtotheoremhook [ ##1 ] { prefoot } { ##2 } } \ProvideDocumentCommand { \addtotheorempostfoothook } { O{allthms} m } { \addtotheoremhook [ ##1 ] { postfoot } { ##2 } } \clist_new:N \l__keythms_tcbshaded_keys_clist \clist_new:N \l__keythms_tcbthmbox_keys_clist \keys_define:nn { keytheorems/thm/shaded } { textwidth .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { width=##1 }, bgcolor .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { colback=##1 }, rulewidth .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { boxrule=##1 }, rulecolor .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { colframe=##1 }, margin .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { boxsep=##1 }, padding .meta:n = { margin=##1 }, leftmargin .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { left~skip=##1 }, rightmargin .code:n = \clist_put_right:Nn \l__keythms_tcbshaded_keys_clist { right~skip=##1 }, } \keys_define:nn { keytheorems/thm/thmbox } { L .code:n = { \clist_put_right:Nn \l__keythms_tcbthmbox_keys_clist { keythms_tcbthmbox_L } }, M .code:n = { \clist_put_right:Nn \l__keythms_tcbthmbox_keys_clist { keythms_tcbthmbox_M } }, S .code:n = { \clist_put_right:Nn \l__keythms_tcbthmbox_keys_clist { keythms_tcbthmbox_S } }, underline .choice:, underline / true .code:n = {}, underline / false .code:n = { \clist_put_right:Nn \l__keythms_tcbthmbox_keys_clist { boxed~title~style={bottomrule=0pt} } }, underline .default:n = true, nounderline .meta:n = { underline=false }, cut .choice:, cut / true .code:n = {}, cut / false .code:n = { \clist_put_right:Nn \l__keythms_tcbthmbox_keys_clist { unbreakable } }, cut .default:n = true, nocut .meta:n = { cut=false }, thickness .code:n = % could also add keys to clist with changed dimens; which is better? { \hook_gput_code:nnn { keytheorems/\l__keythms_thm_envname_tl/prehead } { keythms_tcbox } { \dim_set:Nn \l_keythms_tcbthmbox_thickness_dim { ##1 } } }, leftmargin .code:n = { \hook_gput_code:nnn { keytheorems/\l__keythms_thm_envname_tl/prehead } { keythms_tcbox } { \dim_set:Nn \l_keythms_tcbthmbox_leftmargin_dim { ##1 } } }, rightmargin .code:n = { \hook_gput_code:nnn { keytheorems/\l__keythms_thm_envname_tl/prehead } { keythms_tcbox } { \dim_set:Nn \l_keythms_tcbthmbox_rightmargin_dim { ##1 } } }, hskip .code:n = { \hook_gput_code:nnn { keytheorems/\l__keythms_thm_envname_tl/prehead } { keythms_tcbox } { \dim_set:Nn \l_keythms_tcbthmbox_hskip_dim { ##1 } } }, vskip .code:n = { \hook_gput_code:nnn { keytheorems/\l__keythms_thm_envname_tl/prehead } { keythms_tcbox } { \dim_set:Nn \l_keythms_tcbthmbox_vskip_dim { ##1 } } }, } \dim_new:N \l_keythms_tcbthmbox_thickness_dim \dim_set:Nn \l_keythms_tcbthmbox_thickness_dim { 0.6pt } \dim_new:N \l_keythms_tcbthmbox_leftmargin_dim \dim_set:Nn \l_keythms_tcbthmbox_leftmargin_dim { 0.7\parindent } % use \parindent? thmbox does \dim_new:N \l_keythms_tcbthmbox_rightmargin_dim \dim_set:Nn \l_keythms_tcbthmbox_rightmargin_dim { 0pt } \dim_new:N \l_keythms_tcbthmbox_hskip_dim \dim_set:Nn \l_keythms_tcbthmbox_hskip_dim { 0.2em } \dim_new:N \l_keythms_tcbthmbox_vskip_dim \dim_set:Nn \l_keythms_tcbthmbox_vskip_dim { 0.2em } \msg_new:nnn { keytheorems } { mdframed-undefined } { keytheorems~does~not~define~the~'mdframed'~key.~ Consider~using~the~'tcolorbox'~key~instead. } \keys_define:nn { keytheorems/thm } { shaded .code:n = { \clist_clear:N \l__keythms_tcbshaded_keys_clist \keys_set:nn { keytheorems/thm/shaded } { ##1 } % FIX: surely a better way to do this \RequirePackage{tcolorbox} \pgfkeysifdefined{/tcb/keythms_tcbshaded_default/.@cmd} % even worth it? {} { \tcbset % wish I could do this outside of key but can't assume tcb loaded { keythms_tcbshaded_default/.style= { sharp~corners = all, boxrule = 0pt, left = 0pt, right = 0pt, top = 0pt, bottom = 0pt, parbox = false, } } } \keys_set:ne { keytheorems/thm } { tcolorbox-no-titlebar = { keythms_tcbshaded_default, \l__keythms_tcbshaded_keys_clist } } }, thmbox .code:n = % adapted from https://tex.stackexchange.com/a/236230/208544 { \clist_clear:N \l__keythms_tcbthmbox_keys_clist \keys_set:nn { keytheorems/thm/thmbox } { ##1 } % FIX: surely a better way to do this \RequirePackage{tcolorbox} \tcbuselibrary{skins,breakable} \pgfkeysifdefined{/tcb/keythms_tcbthmbox_default/.@cmd} % even worth it? {} { \tcbset{ keythms_tcbthmbox_default/.style={ enhanced, breakable, sharp~corners=all, right=\l_keythms_tcbthmbox_hskip_dim, left=\l_keythms_tcbthmbox_hskip_dim, top=\l_keythms_tcbthmbox_vskip_dim, bottom=\l_keythms_tcbthmbox_vskip_dim, coltitle=black, frame~engine=empty, interior~titled~engine=empty, interior~engine=empty, extras~broken={ frame~engine=empty, interior~titled~engine=empty, interior~engine=empty }, parbox=false, % even though frame isn't drawn, makes spacing correct boxrule=0.5\l_keythms_tcbthmbox_thickness_dim, attach~boxed~title~to~top~left={ xshift=-\l_keythms_tcbthmbox_leftmargin_dim, }, boxed~title~style={ empty, size=minimal, bottom=0.3ex, top=0ex, % ditto bottomrule=0.5\l_keythms_tcbthmbox_thickness_dim, }, left~skip=\l_keythms_tcbthmbox_leftmargin_dim, right~skip=\l_keythms_tcbthmbox_rightmargin_dim, overlay~unbroken={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (title.south~west) -- (title.south~east); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) |- ([xshift=10mm]frame.south~west); }, overlay~first={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (title.south~west) -- (title.south~east); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) -- (frame.south~west); }, overlay~middle={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) -- (frame.south~west); }, overlay~last={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) |- ([xshift=10mm]frame.south~west); } }, keythms_tcbthmbox_L/.style={ overlay~unbroken={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (title.south~west) -- (title.south~east); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) |- (frame.south~east) -| (frame.north~east); }, overlay~first={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (title.south~west) -- (title.south~east); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) -- (frame.south~west); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~east) -- (frame.south~east); }, overlay~middle={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) -- (frame.south~west); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~east) -- (frame.south~east); }, overlay~last={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) |- (frame.south~east) -| (frame.north~east); } }, keythms_tcbthmbox_M/.style={}, keythms_tcbthmbox_S/.style={ % first and middle same as M overlay~unbroken={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (title.south~west) -- (title.south~east); \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) -- (frame.south~west); }, overlay~last={ \draw[line~width=\l_keythms_tcbthmbox_thickness_dim] (frame.north~west) -- (frame.south~west); } }, } } \keys_set:ne { keytheorems/thm } { tcolorbox = { keythms_tcbthmbox_default, \l__keythms_tcbthmbox_keys_clist } } }, thmbox .default:n = M, mdframed .code:n = \msg_error:nn { keytheorems } { mdframed-undefined }, } \keys_define:nn { keytheorems/thmstyle } { shaded .code:n = \keythms_thmstyle_savethmkey_optval:n { ##1 }, thmbox .code:n = \keythms_thmstyle_savethmkey_optval:n { ##1 }, } } \cs_new_protected:Npn \__keythms_storeall_code: { \cs_set_eq:NN \__keythms_withhooks_begin:nnn \__keythms_grab_begin:nnn \cs_set_eq:NN \__keythms_withhooks_begin:nnV \__keythms_grab_begin:nnV \cs_set_eq:NN \__keythms_withhooks_end:n \__keythms_grab_end:n } \hook_gput_code:nnn { begindocument/before } { . } { % use 'provide' in case user defines their own translation in preamble \ProvideTranslationFallback { keythms_listof_title } { List~of~Theorems } \ProvideTranslationFallback { keythms_continues } { continuing~from~p.\, } \bool_if:NT \g__keythms_autotranslate_bool { \ProvideTranslation { English } { keythms_listof_title } { List~of~Theorems } \ProvideTranslation { English } { keythms_continues } { continuing~from~p.\, } % from DeepL; I don't know these languages! \ProvideTranslation { French } { keythms_listof_title } { Liste~des~théorèmes } \ProvideTranslation { French } { keythms_continues } { suite~de~la~p.\, } \ProvideTranslation { German } { keythms_listof_title } { Liste~der~Theoreme } \ProvideTranslation { German } { keythms_continues } { weiter~von~Seite~ } \ProvideTranslation { Italian } { keythms_listof_title } { Elenco~dei~teoremi } \ProvideTranslation { Italian } { keythms_continues } { continua~da~p.\, } \ProvideTranslation { Spanish } { keythms_listof_title } { Lista~de~teoremas } \ProvideTranslation { Spanish } { keythms_continues } { continúa~de~la~p.\, } \ProvideTranslation { Portuguese } { keythms_listof_title } { Lista~de~teoremas } \ProvideTranslation { Portuguese } { keythms_continues } { continua~da~p.\, } } } \ProcessKeyOptions[keytheorems] %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Support for non-default classes %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \cs_new_protected:Npn \__keythms_support_AMSshared_code: { \keys_define:nn { keytheorems/listof } % adjust to AMS classes { numwidth .initial:n = 1.5pc, indent .initial:n = 0pt, title-code .code:n = { % warn but set titlecmd just in case \msg_warning:nn { keytheorems } { title-code-with-AMS } \cs_set_protected:Npn \__keythms_listof_titlecmd:n ####1 { ##1 } }, } \RenewDocumentCommand \listofkeytheorems { O{} } { % title command not customizable here \bool_gset_true:N \g__keythms_listof_writefile_bool \group_begin: \keys_set:nn { keytheorems/listof } { ##1 } \bool_if:NT \l__keythms_listof_nochapskip_bool { \cs_set_eq:NN \KeyThmsAddvspace \use_none:n } \legacy_if_set_false:n { @filesw } \bool_if:NTF \l__keythms_listof_notitle_bool { % hacky! \cs_set_eq:NN \@startsection \use_none:nnnnnnn \@starttoc{ thlist }{ } } { % ams classes don't expand title enough \protected@edef \l__keythms_tmpa_tl { \l__keythms_listof_title_tl } \bool_if:NT \l__keythms_listof_notoc_bool { \cs_set_eq:NN \addcontentsline \use_none:nnn } % hacky! \@starttoc{ thlist }{ \l__keythms_tmpa_tl } } \group_end: } \cs_new:Npn \__keythms_listof_ams_tocline: { \@tocline{ 0 }{ 3pt plus 2pt }{ \l__keythms_listof_indent_dim } { \l__keythms_listof_numwidth_dim }{ } } \cs_set_eq:NN \keythms_listof_tocline: \__keythms_listof_ams_tocline: } \cs_new_protected:Npn \keythms_support_check:n #1 { \IfClassLoadedT { #1 } { \file_input:n { class-support/keythms-#1-support } } } % acmart defaults also handled in amsart support file \keythms_support_check:n { amsart } \keythms_support_check:n { amsbook } \keythms_support_check:n { amsproc } \keythms_support_check:n { memoir } \file_input_stop: