%%%============================================================================== %% Copyright 2023-present by Alceu Frigeri %% %% This work may be distributed and/or modified under the conditions of %% %% * The [LaTeX Project Public License](http://www.latex-project.org/lppl.txt), %% version 1.3c (or later), and/or %% * The [GNU Affero General Public License](https://www.gnu.org/licenses/agpl-3.0.html), %% version 3 (or later) %% %% This work has the LPPL maintenance status *maintained*. %% %% The Current Maintainer of this work is Alceu Frigeri %% %% This is version {1.4} {2025/05/28} %% %% The list of files that compose this work can be found in the README.md file at %% https://ctan.org/pkg/pgfkeysearch %% %%%============================================================================== \NeedsTeXFormat{LaTeX2e}[2022/06/01] \ProvidesExplPackage {pgfkeysearch} {2025/05/28} {1.4} {pgfkeys Search Extension} \ExplSyntaxOn %%%%%%% %%% %%% Just an attempt of having my packages info in a regular way %%% Idea being: { / pkg info } for each and all. %%% %%%%%%% \keys_define:nn { pgfkeysearch / pkg info} { name .code:n = {pgfkeysearch} , prefix .code:n = {pgfkeysearch} , date .code:n = {2025/05/28}, version .code:n = {1.4} , description .code:n = {pgfkeys~ search~ extension} } \cs_if_exist:NF \PkgInfo { \NewDocumentCommand \PkgInfo {mm} { \keys_set:nn {#1 / pkg info}{#2} } \NewDocumentCommand \PkgDescription {m} { \noindent Package~ \textbf{\PkgInfo{#1}{name}}~Version:~\PkgInfo{#1}{version}~ -~ \PkgInfo{#1}{date}\par \emph{\PkgInfo{#1}{description}}~\par } } %%%%%%% %%% End of cut-n-paste %%%%%%% \msg_new:nnnn {pgfkeysearch} {deprecated} { (ID:#1)~The~ command~#2~is~ deprecated~ use~ #3 ~ instead } { You~tried~to~use~a~deprecated~ command:#2. Use~ #3~ instead. ~Error~Code~ ID:<#1>. } \keys_define:nn { pgfkeysearch } { root~ search .usage:n = general, root~ search .bool_set:N = \l__pgfkeysearch_root_bool, root~ search .initial:n = {false}, } \ProcessKeyOptions [pgfkeysearch] \cs_new_protected:Npn \pgfkeysearch_settings:n #1 { \keys_set:nn {pgfkeysearch}{#1} } %%%%%%%%%%%%%%% %%% %%% _keysearch allows to find a key, %%% if it's defined from 'anywhere' in the tree. I mean, upwards until, likely, /tikz/ %%% if it isn't defined, returns nothing (no error triggered) %%% %%%%%%%%%%%%%%% \seq_new:N \l__pgfkeysearch_pathterms_seq \seq_new:N \l__pgfkeysearch_pathtree_seq \tl_new:N \l__pgfkeysearch_path_tl \bool_new:N \l__pgfkeysearch_keyfound_bool \bool_new:N \l__pgfkeysearch_multipath_keyfound_bool \cs_generate_variant:Nn \tl_set:Nn {Ne} %%%%%%%%%%%%%%% %%% %%% Given a single path /A/B/C/D it will look after %%% /A/B/C/D/ %%% /A/B/C/ %%% /A/B/ %%% /A/ %%% / (if "root search" set) %%% stoping at the first hit %%% %%%%%%%%%%%%%%% \prg_new_protected_conditional:Npnn \pgfkeysearch_keysearch:nnN #1#2#3 {TF,T,F} { \seq_set_split:Nne \l__pgfkeysearch_pathterms_seq {/} {#1} \seq_remove_all:Nn \l__pgfkeysearch_pathterms_seq {} \tl_clear:N \l__pgfkeysearch_path_tl \seq_clear:N \l__pgfkeysearch_pathtree_seq \bool_if:NT \l__pgfkeysearch_root_bool { \seq_put_right:Ne \l__pgfkeysearch_pathtree_seq {} } \seq_map_inline:Nn \l__pgfkeysearch_pathterms_seq { \tl_put_right:Ne \l__pgfkeysearch_path_tl {/##1} \seq_put_right:Ne \l__pgfkeysearch_pathtree_seq {\l__pgfkeysearch_path_tl} } \seq_reverse:N \l__pgfkeysearch_pathtree_seq %\seq_show:N \l__pgfkeysearch_pathtree_seq \bool_set_false:N \l__pgfkeysearch_keyfound_bool \seq_map_inline:Nn \l__pgfkeysearch_pathtree_seq { \cs_if_exist:cT {pgfk@##1/#2} { \tl_set:Ne #3 {\exp_not:N \use:c{pgfk@##1/#2}} \bool_set_true:N \l__pgfkeysearch_keyfound_bool \seq_map_break: } } \bool_if:NTF \l__pgfkeysearch_keyfound_bool { \prg_return_true: } { \prg_return_false: } } \prg_new_protected_conditional:Npnn \pgfkeysearch_keysearch:nnn #1#2#3 {TF,T,F} { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {singlepath}{\cs_to_str:N \pgfkeysearch_keysearch:nnn}{\cs_to_str:N \pgfkeysearch_keysearch:nnN} \pgfkeysearch_keysearch:nnNTF {#1}{#2} #3 { \prg_return_true: } { \prg_return_false: } } %%%%%%%%%%%%%%% %%% %%% #1 shall be a comma separated list of paths (can be a single one) %%% it searchs for a key in every path, stoping at the first hit. %%% %%%%%%%%%%%%%%% \prg_new_protected_conditional:Npnn \pgfkeysearch_multipath_keysearch:nnN #1#2#3 {T,F,TF} { \bool_set_false:N \l__pgfkeysearch_multipath_keyfound_bool \clist_map_inline:nn {#1} { \pgfkeysearch_keysearch:nnNT {##1}{#2} #3 { \bool_set_true:N \l__pgfkeysearch_multipath_keyfound_bool \clist_map_break: } } \bool_if:NTF \l__pgfkeysearch_multipath_keyfound_bool { \prg_return_true: } { \prg_return_false: } } \prg_new_protected_conditional:Npnn \pgfkeysearch_multipath_keysearch:nnn #1#2#3 {T,F,TF} { \msg_warning:nnnxx {pgfkeysearch} {deprecated} {multipath}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnn}{\cs_to_str:N \pgfkeysearch_multipath_keysearch:nnN} \pgfkeysearch_multipath_keysearch:nnNTF {#1}{#2} #3 { \prg_return_true: } { \prg_return_false: } } %%%%%%%%%%%%%%% %%% %%% The caveats: this ins't expandable. %%% %%%%%%%%%%%%%%% %%% %%% This is the more generic one. %%% #1 is the path (or list of paths) %%% #2 is the %%% #3 is the macro that will receive the key value (if any) %%% %%%%%%%%%%%%%%% \NewDocumentCommand{\pgfkeysearchsettings}{m} { \pgfkeysearch_settings:n {#1} } \NewDocumentCommand{\pgfkeysearchvalueof}{mmm} { \pgfkeysearch_multipath_keysearch:nnNF {#1}{#2}#3 { \tl_set:Nn #3 {} } } \let\pgfkeysearch\pgfkeysearchvalueof \NewDocumentCommand{\pgfkeysearchvalueofTF}{mmmmm} { \pgfkeysearch_multipath_keysearch:nnNTF {#1}{#2}#3 { #4 } { \tl_set:Nn #3 {} #5 } } \let\pgfkeysearchTF\pgfkeysearchvalueofTF