pgf-tikz / pgf

A Portable Graphic Format for TeX
https://pgf-tikz.github.io/
1.11k stars 105 forks source link

PGFKeys: Missing \pgfeov in \pgfkeysdefnargs #1350

Closed Qrrbrbirlbel closed 1 month ago

Qrrbrbirlbel commented 1 month ago

Brief outline of the bug

Keys defined by \pgfkeysdefnargs (i.e. .code n args/.style n args) are not delimited by \pgfeov at the end while keys defined with \pgfkeysdefargs (all others) are.

Suggested fix is to include \pgfeov in the .@args as well as in the call to .@@body:

\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\long\def\pgfkeysdefnargs@#1#2#3#4#5{%
  \ifcase#2\relax
    \pgfkeyssetvalue{#1/.@args}{\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3##4\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3##4##5\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3##4##5##6\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3##4##5##6##7\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3##4##5##6##7##8\pgfeov}%
  \or
    \pgfkeyssetvalue{#1/.@args}{##1##2##3##4##5##6##7##8##9\pgfeov}%
  \else
    \pgfkeys@error{\string\pgfkeysdefnargs: expected  <= 9 arguments, got #2}%
  \fi
  \pgfkeysgetvalue{#1/.@args}\pgfkeys@tempargs
  \def\pgfkeys@temp{\expandafter\long\expandafter#4\csname pgfk@#1/.@@body\endcsname}%
  \expandafter\pgfkeys@temp\pgfkeys@tempargs{#3}%
  % eliminate the \pgfeov at the end such that TeX gobbles spaces
  % by using
  % \pgfkeysdef{#1}{\pgfkeysvalueof{#1/.@@body}##1}
  % (with expansion of '#1'):
  \edef\pgfkeys@tempargs{\noexpand\pgfkeysvalueof{#1/.@@body}}%
  \def\pgfkeys@temp{\pgfkeysdef{#1}}%
%  \ifnum#2=1\relax
%    \expandafter\pgfkeys@temp\expandafter{\pgfkeys@tempargs{##1}}%
%  \else
    \expandafter\pgfkeys@temp\expandafter{\pgfkeys@tempargs##1\pgfeov}%
%  \fi
  #5{#1/.@body}{#3}%
}
\makeatother
\begin{document}
\pgfkeys{
  testA/.code n args={2} {\par testA --- 1: #1 -- 2: #2\par}, % \pgfkeysdefnargs
  testB/.code args={#1#2}{\par testB --- 1: #1 -- 2: #2\par}, % \pgfkeysdefargs
  testC/.code 2 args=    {\par testC --- 1: #1 -- 2: #2\par}, % \pgfkeysdefargs
  testA=ABCDEF, testB=ABCDEF, testC=ABCDEF,
  testA/.show code, testA/.@@body/.show value,
  testA/.add code={X}{Y}, testA=ABCDEF,
  testA/.show code, testA/.@@body/.show value,
  test1/.code n args={1}{\par test1 --- 1: #1\par},
  test1=ABC
}
\end{document}

Though, I admit, I don't understand why these get such special treatment with their own .@@body value. Can't this be done directly with \pgfkeys(e)defargs so that it does exactly the same as if we'd have written \pgfkeysdefargs{/key}{#1#2…}{<body>}?

\documentclass{article}
\usepackage{pgfkeys}
\makeatletter
\long\def\pgfkeysdefnargs#1#2#3{\pgfkeysdefnargs@{#1}{#2}{#3}{\pgfkeysdefargs}}%
\long\def\pgfkeysedefnargs#1#2#3{\pgfkeysdefnargs@{#1}{#2}{#3}{\pgfkeysedefargs}}%
\long\def\pgfkeysdefnargs@#1#2#3#4{%
  \ifcase#2\relax
    #4{#1}{}{#3}%
  \or
    #4{#1}{##1}{#3}%
  \or
    #4{#1}{##1##2}{#3}%
  \or
    #4{#1}{##1##2##3}{#3}%
  \or
    #4{#1}{##1##2##3##4}{#3}%
  \or
    #4{#1}{##1##2##3##4##5}{#3}%
  \or
    #4{#1}{##1##2##3##4##5##6}{#3}%
  \or
    #4{#1}{##1##2##3##4##5##6##7}{#3}%
  \or
    #4{#1}{##1##2##3##4##5##6##7##8}{#3}%
  \or
    #4{#1}{##1##2##3##4##5##6##7##8##9}{#3}%
  \else
    \pgfkeys@error{\string\pgfkeysdefnargs: expected  <= 9 arguments, got #2}%
  \fi
}
\makeatother
\begin{document}
\pgfkeys{
  testA/.code n args={2} {\par testA --- 1: #1 -- 2: #2\par}, % \pgfkeysdefnargs
  testB/.code args={#1#2}{\par testB --- 1: #1 -- 2: #2\par}, % \pgfkeysdefargs
  testC/.code 2 args=    {\par testC --- 1: #1 -- 2: #2\par}, % \pgfkeysdefargs
  testA=ABCDEF, testB=ABCDEF, testC=ABCDEF,
  testA/.show code, testA/.@@body/.show value,
  testA/.add code={X}{Y}, testA=ABCDEF,
  testA/.show code, testA/.@@body/.show value,
  test1/.code n args={1}{\par test1 --- 1: #1\par},
  test1=ABC
}
\end{document}

Minimal working example (MWE)

\documentclass{article}
\usepackage{pgfkeys}
\begin{document}
\pgfkeys{
  testA/.code n args={2} {\par testA --- 1: #1 -- 2: #2\par}, % \pgfkeysdefnargs
  testB/.code args={#1#2}{\par testB --- 1: #1 -- 2: #2\par}, % \pgfkeysdefargs
  testC/.code 2 args=    {\par testC --- 1: #1 -- 2: #2\par}, % \pgfkeysdefargs
  testA=ABCDEF, testB=ABCDEF, testC=ABCDEF}
\end{document}

prints

testA — 1: A – 2: B CDEF testB — 1: A – 2: BCDEF testC — 1: A – 2: BCDEF

leaving CDEF in the input stream. (Inside a TikZ Picture this leads to “Missing character” warnings.)

muzimuzhi commented 1 month ago

According to the docs of \pgfkeysdefnargs{<key>}{<arg count>}{<code>} and /.code n args={<arg count>}{<code>}, it seems to me this difference is intentional, hence expected.

Maybe the doc of \pgfkeysdefnargs can be enhanced to explicitly mention that, while similar to \pgfkeysdef, the final argument patten of \pgfkeysdefnargs constructed from <arg count> is not delimited by \pgfeov at the end.

Qrrbrbirlbel commented 1 month ago

Doc of \pgfkeysdefnargs https://github.com/pgf-tikz/pgf/blob/44f8137449b34f62bc6371bd442ae5cc98f60f18/doc/generic/pgf/pgfmanual-en-pgfkeys.tex#L616

Ah, I've missed the text after the example and read only the first sentence: https://github.com/pgf-tikz/pgf/blob/44f8137449b34f62bc6371bd442ae5cc98f60f18/doc/generic/pgf/pgfmanual-en-pgfkeys.tex#L1038-L1039 where it simply says it works “like /.code”.

Maybe the doc of \pgfkeysdefnargs can be enhanced to explicitly mention that, while similar to \pgfkeysdef, the final argument patten of \pgfkeysdefnargs constructed from <arg count> is not delimited by \pgfeov at the end.

It certainly surprised me since I was under the impression everything is delimited in PGFKeys (it kind of still is, of course, because the main /.@cmd still uses \pgfeov but forwards the argment to /.@@body without it).

I agree, it seems intentional – to allow spaces between arguments, I guess.