Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

/handlers/.unknown handler is not called when /.search also fails #671

Closed
schtandard opened this issue May 6, 2019 · 4 comments · Fixed by #914
Closed

/handlers/.unknown handler is not called when /.search also fails #671

schtandard opened this issue May 6, 2019 · 4 comments · Fixed by #914
Labels
Milestone

Comments

@schtandard
Copy link
Contributor

schtandard commented May 6, 2019

The following example can be found on page 969 of the manual:

\documentclass{article}

\usepackage{pgfkeys}

\begin{document}

% define a key:
\pgfkeys{/secondary path/option/.code={Invoking /secondary path/option with ‘#1’}}

% set up a search path:
\pgfkeys{/main path/.search also={/secondary path}}

% try searching for ‘option=value’ in ’/main path’:
% -> this finds ‘/secondary path/option’!
\pgfkeys{/main path/.cd,option=value}

% negative example:
% try searching for fully qualified key /main path/option.
% This won’t be handled by .search also.
\pgfkeys{/handlers/.unknown/.code={Found unknown option \pgfkeyscurrentkeyRAW={#1}!}}%
\pgfkeys{/main path/.cd,/main path/option=value}

\end{document} 

The last \pgfkeys call should trigger /handlers/.unknown. Instead, nothing happens, there isn't even an error. Apparently, when all the elements in the /.search also path list have been checked, the key is just thrown away.

@hmenke hmenke added the pgfkeys label May 6, 2019
@ilayn
Copy link
Member

ilayn commented May 14, 2019

Hi @schtandard. When you write

\pgfkeys{/main path/.cd,/main path/option=value}

you are actually creating a key with full path /main path/main path/option=value because of the first /main path/.cd. If you instead use

\pgfkeys{/main path/.cd,bla=value}

then the handler should work. This is also mixed up with the fact that you can directly set a key with a value regardless of its existence. Hence you have to invoke the right key family for the search.

@hmenke
Copy link
Member

hmenke commented May 14, 2019

No, when \tracingall the log shows that the key /main path/option is being set. Unfortuantely I don't know how to fix it yet. The code is a real mess.

% Prepares the .unknown handler used by '.search also'.
% It will be stored into \pgfkeys@global@temp.
\def\pgfkeys@searchalso@prepare@unknown@handler#1{%
\global\def\pgfkeys@global@temp##1\pgfeov{}%
\pgfkeys@searchalso@parse#1,\pgfkeys@mainstop
{%
\toks0=\expandafter{\pgfkeys@global@temp##1\pgfeov}%
\toks1={\pgfkeysalso{/handlers/.unknown/.@cmd/.expand once=\pgfkeys@searchalso@temp@value}}%
\xdef\pgfkeys@global@temp{%
\noexpand\def\noexpand\pgfkeys@searchalso@temp@value{####1}%
\noexpand\ifpgfkeysaddeddefaultpath
\noexpand\pgfkeyssuccessfalse
\noexpand\let\noexpand\pgfkeys@searchalso@name=\noexpand\pgfkeyscurrentkeyRAW
\the\toks0 % one or more /.try things; one for each path. The last element won't have a /.try
%\noexpand\ifpgfkeyssuccess
%\noexpand\else
% \the\toks1 % invoke /handlers/.unknown handler
%\noexpand\fi
\noexpand\else
\the\toks1 % invoke /handlers/.unknown handler
\noexpand\fi
}%
\expandafter\gdef\expandafter\pgfkeys@global@temp\expandafter##\expandafter1\expandafter\pgfeov\expandafter{\pgfkeys@global@temp}%
}%
}%
\def\pgfkeys@searchalso@parse{\futurelet\pgfkeys@possiblerelax\pgfkeys@searchalso@parse@main}
\def\pgfkeys@searchalso@parse@main{%
\ifx\pgfkeys@possiblerelax\pgfkeys@mainstop%
\expandafter\pgfkeys@cleanup%
\else%
\expandafter\pgfkeys@searchalso@appendentry%
\fi%
}
\def\pgfkeys@searchalso@appendentry#1,#2{%
\def\pgfkeys@searchalso@nexttok{#2}%
\pgfkeys@spdef\pgfkeys@temp{#1}%
{%
\toks0=\expandafter{\pgfkeys@global@temp##1\pgfeov}%
\toks1=\expandafter{\pgfkeys@temp}%
\xdef\pgfkeys@global@temp{%
\the\toks0 % the space is important!
\noexpand\ifpgfkeyssuccess\noexpand\else
\noexpand\pgfqkeys{\the\toks1 }{\noexpand\pgfkeys@searchalso@name
\ifx\pgfkeys@searchalso@nexttok\pgfkeys@mainstop\else/.try\fi /.expand once=\noexpand\pgfkeys@searchalso@temp@value}%
\noexpand\fi}%
\expandafter\gdef\expandafter\pgfkeys@global@temp\expandafter##\expandafter1\expandafter\pgfeov\expandafter{\pgfkeys@global@temp}%
}%
\pgfkeys@searchalso@parse#2%
}
\pgfkeys{%
/handlers/.is family/.code=\pgfkeys{\pgfkeyscurrentpath/.ecode=\edef\noexpand\pgfkeysdefaultpath{\pgfkeyscurrentpath/}},%
/handlers/.cd/.code=\edef\pgfkeysdefaultpath{\pgfkeyscurrentpath/},%
/handlers/.search also/.code={%
\pgfkeys@searchalso@prepare@unknown@handler{#1}%
%\message{I prepared the '\pgfkeyscurrentpath/.unknown' handler \meaning\pgfkeys@global@temp\space for '#1'.}%
\pgfkeyslet{\pgfkeyscurrentpath/.unknown/.@cmd}{\pgfkeys@global@temp}%
}
}%

@ilayn
Copy link
Member

ilayn commented May 14, 2019

Ah I missed the initial /. But setting value behavior change means breaking a lot of code so I don't think you have too many options.

@hmenke hmenke added this to the 3.1.6 milestone Dec 17, 2019
muzimuzhi added a commit to muzimuzhi/pgf that referenced this issue Aug 29, 2020
@muzimuzhi
Copy link
Member

muzimuzhi commented Aug 29, 2020

This problem was introduced by 96e02db#diff-066476085e65c7869b9a0620c8c56f1a in 2013, using the code from patch #19 on sourceforge. That patch aimed to allow unbalanced \if in key values.

The key is the following change made by that commit, in definition of \pgfkeys@searchalso@prepare@unknown@handler,

+   \toks1={\def\pgfutilnext{\pgfkeysvalueof{/handlers/.unknown/.@cmd}##1\pgfeov}\pgfutilnext}%
-   \toks1={\pgfkeysalso{/handlers/.unknown/.@cmd/.expand once=\pgfkeys@searchalso@temp@value}}%
  1. Currently, #1 of \pgfkeys@searchalso@prepare@unknown@handler is stored in \pgfkeys@searchalso@temp@value.
  2. Currently, \pgfkeysalso{/handlers/.unknown/.@cmd/.expand once=\pgfkeys@searchalso@temp@value} is used when unknown full key is encountered. But that code actually redefines /handlers/.unknown/.@cmd. This leads to the nothing-happened problem. Removing /.@cmd solves the problem.
  3. There are problems remained in \pgfkeysalso{/handlers/.unknown/.expand once=\pgfkeys@searchalso@temp@value}.
    • \pgfkeysalso parses its key-list argument and updates all current-key macros, for example \pgfkeyscurrentkeyRAW. This updating makes \pgfkeys{/main path/.cd,/main path/option=value} produce text Found unknown option /handlers/.unknown=value!, while the expected output is Found unknown option /main path/option=value!.
    • To avoid updating current-key macros, considering \toks1 is used in \noexpand\else\the\toks1 \noexpand\fi, I use some tricks to expand the trailing \fi first. See the following full example.

Full example

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{xpatch}

\makeatletter
\xpatchcmd\pgfkeys@searchalso@prepare@unknown@handler
  {\toks1={\pgfkeysalso{/handlers/.unknown/.@cmd/.expand once=\pgfkeys@searchalso@temp@value}}}
  {%
  	\toks1={%
      \pgfkeysgetvalue{/handlers/.unknown/.@cmd}{\pgfkeys@code}%
      % trick:
      % to allow unbalanced \if values (stored in \pgfkeys@searchalso@temp@value),
      % expand the \fi right after "\the\toks1 " first, see below
      \expandafter\expandafter\expandafter\pgfkeys@code\expandafter\pgfkeys@searchalso@temp@value\expandafter\pgfeov
    }%
  }
  {}{\fail}
\makeatother

\begin{document}
\parindent=0pt

\subsection*{Test issue \#671, \texttt{.search also} with unknown full key}
% define a key:
\pgfkeys{/secondary path/option/.code={Invoking /secondary path/option with ‘#1’}}

% set up a search path:
\pgfkeys{/main path/.search also={/secondary path}}

% try searching for ‘option=value’ in ’/main path’:
% -> this finds ‘/secondary path/option’!
\pgfkeys{/main path/.cd, option=value}

% negative example:
% try searching for fully qualified key /main path/option.
% This won’t be handled by .search also.
\pgfkeys{/handlers/.unknown/.code={Found unknown option \pgfkeyscurrentkeyRAW={#1}!}}%
\pgfkeys{/main path/.cd, /main path/option=value}


\subsection*{Test patch \#19, unbalanced \texttt{\char`\\if} values}

% example is from https://sourceforge.net/p/pgf/patches/19/
\pgfkeys{
  rules/.cd,
  define rule/.code={DEFINING RULE: \detokenize{#1}\par},
  special/.search also=/rules,
  % We get an error if the following line is uncommented.
  special/.cd,
  define rule={\ifnum\answer=42\relax}, % `mild' error: "(\end occurred when \ifx on line 12 was incomplete)"
  define rule={\fi}, % fatal
}
\end{document} 

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

Successfully merging a pull request may close this issue.

4 participants