I expect a user to write something like:
\pgfkeys{
latex/.append style={
add to preamble={
\def\sayhello#1{Hello #1.}
}
},
}
but this does not work, as #1 is understood as the first argument of latex’s style while I would expect it to be just the symbol #. So I need to double the number of hashes, like:
\pgfkeys{
latex/.append style={
add to preamble={
\def\sayhello##1{Hello ##1.}
}
},
}
However, there is no reason to define a #1, since latex expects anyway no argument.
Is it possible, somehow, to say that add to preamble should not try to interpret the inner hashes as arguments? I guess it might be possible to play with catcodes, but importantly these catcodes should be added once for all in the add to preamble definition but it's not a code that I want the end user to type (basically they should type more or less exactly what I wrote in the first code).
MWE
\documentclass[]{article}
\usepackage{tikz}
\begin{document}
\ExplSyntaxOn
%% See also https://tex.stackexchange.com/questions/695432/latex3-latex-doubles-the-number-of-hashes-when-storing-them-in-string/695460#695460
\cs_generate_variant:Nn \str_replace_all:Nnn { Nnx }
\cs_set:Nn \str_set_hash_robust:Nn {
\str_set:Nn {#1} {#2}
\str_replace_all:Nnx {#1} { ## } { \c_hash_str }
}
\NewDocumentCommand{\defineString}{m}{
\str_set_hash_robust:Nn \l_test_str {#1}
}
\NewDocumentCommand{\showString}{}{
\show \l_test_str
}
\ExplSyntaxOff
% To show that in a normal setting hashes are not doubled
\defineString{Hey \notexistingbutnoproblem #}
\showString
%%% Code of the library: feel free to do anything here
\pgfkeys{
latex/.style={
add to preamble/.code={\defineString{##1}},
show string/.code={\showString},
},
add to style/.style 2 args={
#1/.append style={
#2
},
},
}
%%% Code of the user: ideally modify as little as possible, especially no catcode stuff.
%%% I’m fine with defining "my append to style={latex}{add to preamble={...}}" but no catcode stuff here.
\pgfkeys{
%% I don't want the user to use ##, a single # should be enough.
%%
add to style={latex}{
%% This should contain only # instead of ##
% Works:
add to preamble={\def\sayhello##1{Hello ##1}}
% Fails:
% add to preamble={\def\sayhello#1{Hello #1}}
},
latex,
show string
}
\end{document}
EDIT
I found (EDIT2 ACTUALLY IT IS NOT WORKING PERFECTLY AS IF THE CODE CONTAINS PLACEHOLDERS THEY ARE REPLACED AS WELL AND CAN BREAK THE SYSTEM, I NEED A MORE SPECIFIC REPLACEMENT FUNCTION) a solution by replacing latex/.append style={…} with a custom stuff I created add to style={latex}{…}. This converts the second argument to a string, then it converts # to ##, put it in a strings, create (using placeholders with my lib robustExternalize, with expanded after I would get extra {…} around the string not sure why, certainly catcode magic). Then I add \pgfkeys-stuff around, and I rescan this string (using again robustExternalize)… I guess there is a better solution, but it is the only solution I found for now.
\documentclass[]{article}
\usepackage{tikz}
\usepackage{robust-externalize} % Allows manipulation with placeholder, not even sure how to do it efficiently without. Copy .sty from https://github.com/leo-colisson/robust-externalize (freshly added in CTAN)
\begin{document}
\ExplSyntaxOn
%% See also https://tex.stackexchange.com/questions/695432/latex3-latex-doubles-the-number-of-hashes-when-storing-them-in-string/695460#695460
\cs_generate_variant:Nn \str_replace_all:Nnn { Nnx }
\cs_set:Nn \str_set_hash_robust:Nn {
\str_set:Nn {#1} {#2}
\str_replace_all:Nnx {#1} { ## } { \c_hash_str }
}
\cs_generate_variant:Nn \str_set_hash_robust:Nn { cn }
% Double the number of hashes... quite dirty but cannot find any solution or the user need to double it itself
%
\cs_set:Nn \str_set_hash_double:Nn {
\str_set:Nn {#1} {#2}
\str_replace_all:Nnx {#1} { ## } { \c_hash_str \c_hash_str }
}
\NewDocumentCommand{\defineString}{m}{
\str_set_hash_robust:Nn \l_test_str {#1}
}
\NewDocumentCommand{\strSetDoubleHash}{mm}{
\str_set:Nn {#1} {#2}
\str_replace_all:Nnx {#1} { ## } { \c_hash_str \c_hash_str }
}
\NewDocumentCommand{\showString}{}{
\show \l_test_str
}
\ExplSyntaxOff
% To show that in a normal setting hashes are not doubled
\defineString{Hey \notexistingbutnoproblem #}
\showString
%%% Code of the library: feel free to do anything here
\pgfkeys{
latex/.style={
add to preamble/.code={\defineString{##1}},
show string/.code={\showString},
},
add to style/.code 2 args={%
\strSetDoubleHash{\robExtTmpStr}{#2}
% Sadly, \expanded{\noexpand } does not work, as I get extra {} around the def, creating a group
% so the simpler seems to use this library ^^
\robExtPlaceholderFromString{ROBEXT_TMP}{\robExtTmpStr}%
\robExtEvalPlaceholder{%
\pgfkeys{%
#1/.append style={ROBEXT_TMP},%
}%
}%
\robExtRemovePlaceholder{ROBEXT_TMP}% let us clean our variables
},
}
%%% Code of the user: ideally modify as little as possible, especially no catcode stuff.
%%% I’m fine with defining "my append to style={latex}{add to preamble={...}}" but no catcode stuff here.
\pgfkeys{
%% I don't want the user to use ##, a single # should be enough.
%%
add to style={latex}{
add to preamble={\def\sayhello#1{Hello #1}\foo},
},
latex,
show string
}
\end{document}
If you know a better solution, please let me know ^^
\def\sayhello#1{Hello #1}supposed to be used as it is in the document or will it get written to an file to be read later? In the latter, a definition ofadd to style/.style 2 args={#1/.append style/.expanded=\detokenize{#2}}will have\l_test_strcontain\def \sayhello #1{Hello #1}. – Qrrbrbirlbel Sep 08 '23 at 19:17/robExt/{ some keydoes not exists. I need to investigate it further when I get back to a computer. Regarding the other question, it seems to do the other direction but I'm not 100% sure it does not help here. And the goal is to have the def put in a string, and this string may be written to a file or evaluated depending on whether externalization is enabled or not. – tobiasBora Sep 09 '23 at 10:29