% \iffalse meta-comment % %% File: fancytm.dtx % % Copyright (C) 2022 Maximilian Keßler % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this % license or (at your option) any later version. The latest version % of this license is in the file % % https://www.latex-project.org/lppl.txt % % ----------------------------------------------------------------------- %\ProvidesExplPackage{fancythm}{2022/01/17}{0.0.1}{Grouped theorems.} % %<*driver> \documentclass[full,kernel]{l3doc} \usepackage{mkessler-todo} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \title{^^A % The \pkg{fancythm} package^^A % } % % \author{^^A % Maximilian Keßler % } % % \date{Released 2022-01-12} % % \maketitle % % \begin{documentation} % \end{documentation} % % % \begin{implementation} % % \section{\pkg{fancythm} implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=fancythm> % \end{macrocode} % % % \subsection{Dependencies} % \begin{macrocode} \RequirePackage{l3keys2e} \RequirePackage{groupthm} \RequirePackage[default styles]{mkessler-thmstyle} % \end{macrocode} % % \subsection{Counter management} % % % Wrappers for \LaTeX2e counter manipulation % % % \begin{macro}{\arabic:n, \counter_new:n, \counter_within:nn,\counter_new:nn} % % Obvious meanings. % % \begin{macrocode} \cs_set_eq:NN \arabic:n \arabic \cs_set_eq:NN \counter_new:n \newcounter \cs_set_eq:NN \counter_within:nn \counterwithin \cs_new:Npn \counter_new:nn #1 #2 { \counter_new:n { #1 } \counter_within:nn { #1 } { #2 } } % \end{macrocode} % \end{macro} % % % % % \begin{macro} % { % \counter_if_exist_p:n, % \counter_if_exist:nT, % \counter_if_exist:F, % \counter_if_exist:nTF, % } % % % % \begin{macrocode} \cs_new:Npn \counter_if_exist_p:n #1 { \cs_if_exist_p:c { c@ #1 } } \cs_new:Npn \counter_if_exist:nT #1 { \cs_if_exist:cT { c@ #1 } } \cs_new:Npn \counter_if_exist:nF #1 { \cs_if_exist:cF { c@ #1 } } \cs_new:Npn \counter_if_exist:nTF #1 { \cs_if_exist:cTF { c@ #1 } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\counter_undefined_error:n} % % Standard counter error message. % % \begin{macrocode} \cs_set_eq:Nc \counter_undefined_error:n { @nocounterr } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\counter_ensure_exist:n} % % % % \begin{macrocode} \cs_new:Npn \counter_ensure_exist:n #1 { \counter_if_exist:nF { #1 } { \counter_undefined_error:n { #1 } } } % \end{macrocode} % \end{macro} % % % % % % \begin{macro}{\counter_the:n, \counter_set_the:nn} % % % \begin{macrocode} \cs_new:Npn \counter_the:n #1 { \use:c { the #1 } } \cs_new:Npn \counter_set_the:nn #1 #2 { \counter_ensure_exist:n { #1 } \exp_args:Nc \renewcommand { the #1 } { #2 } } % \end{macrocode} % \end{macro} % % % % % \begin{macro}{\counter_sub:nn} % \begin{syntax} % \cs{counter_sub:nn}\marg{counter_1}\marg{counter_2} % \end{syntax} % % % % \begin{macrocode} \cs_new:Npn \counter_sub:nn #1 #2 { \counter_within:nn { #1 } { #2 } \counter_set_the:nn { #1 } { \counter_the:n { #2 } . \arabic:n { #1 } } } % \end{macrocode} % \end{macro} % % % \begin{macro}{\counter_new_sub:nn #1 #2} % % % % \begin{macrocode} \cs_new:Npn \counter_new_sub:nn #1 #2 { \counter_new:n { #1 } \counter_sub:nn { #1 } { #2 } } % \end{macrocode} % \end{macro} % % \subsection{Counter management} % % % % \begin{macro}{\@@_counter_sub_if_exist:n} % % % % \begin{macrocode} \cs_new:Npn \@@_counter_sub_if_exist:n #1 { \counter_if_exist:nT { #1 } { \counter_new_sub:nn { in #1 } { #1 } } } % \end{macrocode} % \end{macro} % % % % % % % \begin{macro}{\counter_alias:nn, \counter_alias:nx} % \begin{syntax} % \cs{counter_alias:nn}\marg{counter_1}\marg{counter_2} % \end{syntax} % % % % \begin{macrocode} \cs_set_eq:Nc \counter_alias:nn { @counteralias } \cs_generate_variant:Nn \counter_alias:nn { n x } % \end{macrocode} % \end{macro} % % % % % % % \subsection{Initialization} % % % % \begin{variable}{indocument, insection, insubsection, inchapter, inpart} % % % % \begin{macrocode} \counter_new:n { indocument } \@@_counter_sub_if_exist:n { section } \@@_counter_sub_if_exist:n { subsection } \@@_counter_sub_if_exist:n { chapter } \@@_counter_sub_if_exist:n { part } % \end{macrocode} % \end{variable} % % \begin{variable}{toplevel, sublevel, subsublevel, subsubsublevel} % % Note that the \texttt{toplevel} counter will be potentially redefined % by the given package options. % % \begin{macrocode} \counter_alias:nn { toplevel } { indocument } \counter_new_sub:nn { sublevel } { toplevel } \counter_new_sub:nn { subsublevel } { sublevel } \counter_new_sub:nn { subsubsublevel } { subsublevel } % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_style_str} % % This will hold the style information of the package. % % \begin{macrocode} \str_new:N \g_@@_style_str % \end{macrocode} % \end{variable} % % \begin{variable}{\g_@@_translator_bool} % % % % \begin{macrocode} \bool_new:N \g_@@_translator_bool % \end{macrocode} % \end{variable} % % % \begin{variable}{\g_@@_generate_defaults_bool} % % % % \begin{macrocode} \bool_new:N \g_@@_generate_defaults_bool % \end{macrocode} % \end{variable} % % % % % \begin{variable} % { % \l_@@_key_name_tl, % \l_@@_key_mdframed_tl, % \l_@@_key_style_tl, % \l_@@_key_group_clist, % \l_@@_key_thmtools_clist, % } % % % \begin{macrocode} \tl_new:N \l_@@_key_name_tl \tl_new:N \l_@@_key_mdframed_tl \tl_new:N \l_@@_key_style_tl \clist_new:N \l_@@_key_group_clist \clist_new:N \l_@@_key_thmtools_clist % \end{macrocode} % \end{variable} % % % % \begin{variable} % { % \l_@@_name_tl, % \l_@@_thmtools_clist, % \l_@@_group_clist, % } % % % \begin{macrocode} \tl_new:N \l_@@_name_tl \clist_new:N \l_@@_thmtools_clist \clist_new:N \l_@@_group_clist % \end{macrocode} % \end{variable} % % \subsection{Key interface} % % \begin{texnote} % Note that unfortunately, none of the keynames really containes a space. % \LaTeX2e strips spaces before loading a package, so introducing them here % would make them inaccessible. % Here they are ignored by \LaTeX3 and are present for readability. % \end{texnote} % % % \begin{macrocode} \keys_define:nn { fancythm } { translator .bool_set:N = \g_@@_translator_bool , translator .default:n = { true } , generate defaults .bool_set:N = \g_@@_generate_defaults_bool , generate defaults .default:n = { true } , number in .choices:nn = { document, section, subsection, chapter, part } { \counter_alias:nx { toplevel } { in \tl_use:N \l_keys_choice_tl } } , number in .default:n = { document } , style .choices:nn = { fancy, plain, classic } { \str_set:Nn \g_@@_style_str { \tl_use:N \l_keys_choice_tl } } , style .default:n = { fancy } , } % \end{macrocode} % % % % % % % \begin{macrocode} \keys_define:nn { fancythm / fancytheorem } { name .tl_set:N = \l_@@_key_name_tl , name .default:n = \c_novalue_tl , mdframed .tl_set:N = \l_@@_key_mdframed_tl , mdframed .default:n = \c_novalue_tl , style .tl_set:N = \l_@@_key_style_tl , style .default:n = \c_novalue_tl , group .clist_set:N = \l_@@_key_group_clist , group .default:n = {} , thmtools .clist_set:N = \l_@@_key_thmtools_clist , thmtools .default:n = {} , } \keys_define:nn { @@ / providedtheorem } { name .tl_set:N = \l_@@_key_name_tl , name .default:n = \c_novalue_tl , mdframed .tl_set:N = \l_@@_key_mdframed_tl , mdframed .default:n = \c_novalue_tl , style .tl_set:N = \l_@@_key_style_tl , style .default:n = \c_novalue_tl , group .clist_set:N = \l_@@_key_group_clist , group .default:n = {} , thmtools .clist_set:N = \l_@@_key_thmtools_clist , thmtools .default:n = {} , } % \end{macrocode} % % % Process the given keys: % % \begin{macrocode} \ProcessKeysOptions{ fancythm } % \end{macrocode} % % % % % This sets up translation if requested. % Throughout implementation, we can just use \cs{@@_translate:n} % and will (or not) have translation according to the specified options. % % \begin{macrocode} \bool_if:NTF \g_@@_translator_bool { \RequirePackage{translator} \usedictionary{translator-environment-names} \cs_set_eq:NN \@@_translate:n \translate } { \cs_set_eq:NN \@@_translate:n \use:n } % \end{macrocode} % % % % % \begin{macro}{\@@_set_normalized_keys:nn} % % \begin{syntax} % \cs{@@_set_normalized_keys:nn}\marg{keys}\marg{fallback name} % \end{syntax} % % % \begin{macrocode} \cs_new:Npn \@@_set_normalized_keys:nn #1 #2 { \keys_set:nn { fancythm / fancytheorem } { name, mdframed, style, group, thmtools } \keys_set:nn { fancythm / fancytheorem } { #1 } \clist_set_eq:NN \l_@@_group_clist \l_@@_key_group_clist \clist_set_eq:NN \l_@@_thmtools_clist \l_@@_key_thmtools_clist \tl_if_eq:NnF \l_@@_key_mdframed_tl { \c_novalue_tl } { \clist_put_right:Nx \l_@@_thmtools_clist { mdframed = { style = \tl_use:N \l_@@_key_mdframed_tl } } } \tl_if_eq:NnF \l_@@_key_style_tl { \c_novalue_tl } { \clist_put_right:Nx \l_@@_thmtools_clist { style = \tl_use:N \l_@@_key_style_tl } } \tl_if_eq:NnTF \l_@@_key_name_tl { \c_novalue_tl } { \tl_set:Nx \l_@@_name_tl { \text_titlecase_first:n { \tl_trim_spaces:n { #2 } } } } { \tl_set_eq:NN \l_@@_name_tl \l_@@_key_name_tl } \tl_set:Nx \l_@@_name_tl { \exp_not:N \@@_translate:n { \tl_use:N \l_@@_name_tl } } } % \end{macrocode} % \end{macro} % % % % % % \subsection{Fancy theorems} % % % % \begin{macro}{\@@_wrap_multiple:nnn} % \begin{syntax} % \cs{@@_wrap_multiple:nnn}\marg{declarator list}\marg{function name}\marg{code} % \end{syntax} % % Defines \meta{function name}, which is assumed to contain \cs{declarator} % by \meta{code} for each declarator in \meta{declarator list}. % % \begin{macrocode} \cs_new:Npn \@@_wrap_multiple:nnn #1 #2 #3 { \cs_set:Npn \@@_map_aux:n ##1 { \cs_new:cn { #2 } { #3 } } \clist_map_function:nN { #1 } \@@_map_aux:n } % \end{macrocode} % \end{macro} % % % \begin{macro}{\fancythm_new_theorem:nnnn, \fancythm_provide_theorem:nnnn} % \begin{syntax} % \cs{fancythm_new_theorem:nnnn}\marg{fancy theorem}\marg{groups} % \marg{name}\marg{thmtools keys} % \end{syntax} % % % % \begin{macrocode} \@@_wrap_multiple:nnn { new, provide } { fancythm_#1_theorem:nnnn } { \use:c { groupthm_#1_family:nnnnn } { ##1 } { dagger, star } { ##3 } { ##4 } { ##2 } \use:c { groupthm_#1_family_options:nnnn } { ##1 } { !s !t+ } { \IfBooleanT { ####1 } { \groupthm_add_theorem_to_group:n { star } } \IfBooleanT { ####2 } { \groupthm_add_theorem_to_group:n { dagger } } } { ##2 } } \cs_generate_variant:Nn \fancythm_new_theorem:nnnn { n V V V } \cs_generate_variant:Nn \fancythm_provide_theorem:nnnn { n V V V } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\fancythm_new_theorem:nn} % \begin{syntax} % \cs{fanythm_new_theorem:nn}\marg{key=value list}\marg{fancy theorem} % \end{syntax} % % % % \begin{macrocode} \@@_wrap_multiple:nnn { new, provide } { fancythm_#1_theorem:nn } { \@@_set_normalized_keys:nn { ##1 } { ##2 } \use:c { fancythm_#1_theorem:nVVV } { ##2 } \l_@@_group_clist \l_@@_name_tl \l_@@_thmtools_clist } % \end{macrocode} % \end{macro} % % % \begin{macro}{\@@_new_document_command:Nnn, \@@_new_document_command:cnn} % % Private wrappers around \cs{NewDocumentCommand}. % % \begin{macrocode} \cs_new:Npn \@@_new_document_command:Nnn #1 #2 #3 { \NewDocumentCommand { #1 } { #2 } { #3 } } \cs_generate_variant:Nn \@@_new_document_command:Nnn { c n n } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_wrap_multiple_document:nnnn} % \begin{syntax} % \cs{@@_wrap_multiple_document:nnnn}\marg{declarator list}\marg{function name}\marg{arg spec}\marg{code} % \end{syntax} % % This is very similar to \cs{@@_wrap_multiple:nnn}, except that it produces document commands. % For this reason, \cs{declarator} and \cs{Declarator} are available to refer to the lower % and upper-case versions of the current declarator. % % \begin{macrocode} \cs_new:Npn \@@_wrap_multiple_document:nnnn #1 #2 #3 #4 { \cs_set:Npn \@@_map_aux:n ##1 { \cs_set:Nn \@@_Declarator: { \text_titlecase_first:n { ##1 } } \@@_new_document_command:cnn { #2 } { #3 } { #4 } } \clist_map_function:nN { #1 } \@@_map_aux:n } % \end{macrocode} % \end{macro} % % % % % % % \begin{macro}{\NewFancyTheorem, \ProvideFancyTheorem} % \begin{syntax} % \cs{NewFancyTheorem}\marg{key=value list}\marg{fancy theorem} % \end{syntax} % % % % \begin{macrocode} \@@_wrap_multiple_document:nnnn { new, provide } { \@@_Declarator: FancyTheorem } { O{} m } { \use:c { fancythm_#1_theorem:nn } { ##1 } { ##2 } } % \end{macrocode} % \end{macro} % % % % \subsection{Provided resources} % % % % % We introduce various new 0theorem groups that help us to organize the document % in a flexible way. % % \begin{variable}{star, dagger, big, small, tiny, custom} % % % \begin{macrocode} \groupthm_new_group:nnnnn { star } { } { * } { } { } \groupthm_new_group:nnnnn { dagger } { } { $^{\dagger}$ } { } { } \groupthm_new_group:nnnnn { big } { } { } { } { sibling = toplevel } \groupthm_new_group:nnnnn { small } { } { } { } { sibling = sublevel } \groupthm_new_group:nnnnn { tiny } { } { } { } { numbered = no } \groupthm_new_group:nnnnn { custom } { } { } { } { sibling = sublevel } % \end{macrocode} % \end{variable} % % % \begin{macrocode} \groupthm_add_parent:nn { star } { custom } \groupthm_add_parent:nn { dagger } { custom } % \end{macrocode} % \begin{macrocode} \DeclareTheoremGroupRule [ suffix ] { dagger } { higher } { star } \DeclareTheoremGroupRule { tiny } { higher } { small } \DeclareTheoremGroupRule { tiny } { higher } { big } \DeclareTheoremGroupRule { tiny } { higher } { custom } \DeclareTheoremGroupRule { small } { higher } { big } \DeclareTheoremGroupRule { custom } { higher } { big } % \end{macrocode} % % % It remains to provide a list of theorems at the beginning of the document. % % % \begin{macro}{\fancythm_add_provided_theorem_to_group:nn} % % \begin{macrocode} \cs_new:Npn \fancythm_add_provided_theorem_to_group:nn #1 #2 { \cs_if_exist:cF { @@_provided_theorem__#1__group_clist } { \clist_new:c { @@_provided_theorem__#1__group_clist } } \clist_put_left:cn { @@_provided_theorem__#1__group_clist } { #2 } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\AddProvidedFancyTheoremToGroup} % % % % \begin{macrocode} \NewDocumentCommand { \AddProvidedFancyTheoremToGroup } { m m } { \fancythm_add_provided_theorem_to_group:nn { #1 } { #2 } } % \end{macrocode} % \end{macro} % % % % \begin{macro}{\@@_provide_package_theorem:nnn} % \begin{syntax} % \cs{@@_provide_package_theorem:nnn}\marg{key=value list}\marg{fancy theorem} % \marg{group} % \end{syntax} % % Group can be one of \texttt{big}, \texttt{small}, \texttt{tiny}, % and the provided theorem will have this group, unless it conflicts % with a user-provided group, in which case this is ignored. % % % \begin{macrocode} \cs_new:Npn \@@_provide_package_theorem:nnn #1 #2 #3 { \@@_set_normalized_keys:nn { #1 } { #2 } \cs_if_exist:cT { @@_provided_theorem__#2__group_clist } { \clist_concat:ccc { l_@@_group_clist } { @@_provided_theorem__#2__group_clist } { l_@@_group_clist } } \clist_if_in:NnF \l_@@_group_clist { big } { \clist_if_in:NnF \l_@@_group_clist { small } { \clist_if_in:NnF \l_@@_group_clist { tiny } { \clist_put_right:Nn \l_@@_group_clist { #3 } } } } \fancythm_provide_theorem:nVVV { #2 } \l_@@_group_clist \l_@@_name_tl \l_@@_thmtools_clist } % \end{macrocode} % \end{macro} % % % % % % \begin{macrocode} \AddToHook { begindocument / before } [ fancythm ] { \@@_provide_package_theorem:nnn { style = thmgreenmargin } { example } { big } \@@_provide_package_theorem:nnn { style = thmorangemarginandfill} { lemma } { big } } % \end{macrocode} % % % % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % % % \newpage