3

Expansion gives me headaches. I could use some help. The problem is identified under the heading: Log Output Snippet AtEndAfterFileList.

This is an extension of my context-based log entry question:

How can I write <section counter value>:<section title>:<page>:<file>:<line> to the log whenever (and whereever) a macro is called?

Goal

I want the following context-specific macros to be expanded before they are appended to \trackedmacrolist, because I don't want to call \trackedmacrolist until the very end.

\thenumber:\thetitle:[\thepage]:\currfilepath:\the\inputlineno

Code

\listfiles
\documentclass{article}
\usepackage{atveryend}
\usepackage{currfile}% adds \currfilepath

% Define a couple commands to track
\DeclareRobustCommand{\myproduct}[1]{MacGyver.\textit{#1}}

% The following is normally put into separate file as to modularize the application of patches
% Define a context macro as described
\def\contextinfo{\thenumber:\thetitle:[\thepage]:\currfilepath:\the\inputlineno}
\def\trackedmacro{Tracked Macro List}% initialize macro
\AtEndAfterFileList{\typeout{\trackedmacro}}

\makeatletter
% Apply Patches
\usepackage{regexpatch}
\xapptocmd{\myproduct}{\typeout{MacMessage: \contextinfo}\g@addto@macro{\trackedmacro}{^^J==> #1 (\contextinfo)}}{}{}% <-- I'd rather show MacGyver.monkey shampoo, but unsure how best to implement
% Note: \typeout is always fully expanded due to \write %https://tex.stackexchange.com/a/60976/13552
\makeatother

%\usepackage{xparse}% Loaded by regexpatch
\let\oldsection\section
\makeatletter
\RenewDocumentCommand{\section}{s o m}{%
  \begingroup
  % Extract the counter representation
  \stepcounter{section}%
  \xdef\thenumber{\thesection}%
  \addtocounter{section}{-1}%
  \protected@xdef\thetitle{#3}%
  \endgroup
  % Regular section
  \IfBooleanTF{#1}
    {\oldsection*{#3}}
    {\IfValueTF{#2}
       {\oldsection[#2]{#3}}
       {\oldsection{#3}}}
}
\makeatother

\begin{document}

\section{About \myproduct{elephant shampoo}}
\section{About \myproduct{monkey shampoo}}
\section{About \myproduct{giraffe shampoo}}
\section{About \myproduct{tiger shampoo}}

\end{document}

Log Output Snippet running xelatex example.tex | grep MacMessage

Looks good here!

MacMessage: (1:About \myproduct {elephant shampoo}:[1]:example.tex:44)
MacMessage: (2:About \myproduct {monkey shampoo}:[1]:example.tex:45)
MacMessage: (3:About \myproduct {giraffe shampoo}:[1]:example.tex:46)
MacMessage: (4:About \myproduct {tiger shampoo}:[1]:example.tex:47)

Log Output Snippet AtEndAfterFileList

Not so good here

Tracked Macro List
==> elephant shampoo (4:About \myproduct {tiger shampoo}:[2]:example.tex:49)
==> monkey shampoo (4:About \myproduct {tiger shampoo}:[2]:example.tex:49)
==> giraffe shampoo (4:About \myproduct {tiger shampoo}:[2]:example.tex:49)
==> tiger shampoo (4:About \myproduct {tiger shampoo}:[2]:example.tex:49)

As you can see compared to the above output, the page numbers no longer correspond to where the macros exist in the code, rather to where the macros were first expanded: At the very end when \AtEndAfterFileList was called.

Issues:

  • All macros show section 4, which is the latest expansion \thenumber at the time of \end{document} where \AtEndAfterFileList is hooked to.
  • All macros expand to \myproduct{tiger shampoo}, which is the latest expansion of \thetitle at the time of \end{document} where \AtEndAfterFileList is hooked to.
  • All macros show page 2, which is the latest expansion of \thepage at the time of \end{document} where \AtEndAfterFileList is hooked to.
  • All macros show line 49, which is the latest expansion of \the\inputlineno at \end{document} where \AtEndAfterFileList is hooked to.

Note that if I had an \input at some point before that last \myproduct call, I would expect a problem with the \currfilepath too.

Related

  • Since TeX never expands macros when absorbing arguments, it's unclear what you're expecting. – egreg Jul 7 '16 at 13:26
  • I don't understand this question. Using \myproduct in the section title will apply \myproduct twice -- in the body and when the ToC is written -- do you really want to have this information? – user31729 Jul 7 '16 at 13:28
  • Ok, I made it clearer, I hope. – Jonathan Komar Jul 7 '16 at 13:37
  • I think, the \thetitle is the cause of your problem. – user31729 Jul 7 '16 at 14:09
  • @ChristianHupfer Yes, that i no problem. \myproduct is just a way of creating lists of anything wrapped with that macro. If a product is obsolete, it can be quicker to identify obsolete products in the log output than reading the entire pdf. (both visually and programmatically). In reality I am generating quite an elaborate logging package called docdiagnostics :). In a production environment where things are tested, maintenance costs have become an increasingly important topic. I am determined to demonstrate that (Xe)LaTeX is the tool for the job. – Jonathan Komar Jul 7 '16 at 14:11
5

I think it's difficult to understand the question, it's just too cryptic until one understands it :)

I'm going to try to answer this in a constructive way. Your problem is

\g@addto@macro{\trackedmacro}{^^J==> #1 (\contextinfo)}

that doesn't expand the \contextinfo, so in the end you have \contextinfo .. \contextinfo .. \contextinfo .. \contextinfo all inside \trackedmacro and all of them expand at the same time (hence expanding to the same thing). So that's your particular problem. You need to ensure that it get's expanded before appending to \trackedmacro.

You could do it this way First, you need to expand it completely, so

\protected@edef\tmp{\contextinfo}

will save the already expanded content inside \tmp. Now you need to expand \tmp once before putting it inside \tracketmacro so you get the whole value. This will do it

\expandafter\addtotrackedmacro\expandafter{\tmp}{#1}

with the appropiate definition

\newcommand*\addtotrackedmacro[2]{\g@addto@macro{\trackedmacro}{^^J==> #2 (#1)}}

I don't mean that this is the optimal solution to your problem. But starting from the code you got, you have an easy and small solution to that problem. Now just changing this line in your code

\xapptocmd{\myproduct}{\typeout{MacMessage: \contextinfo}\g@addto@macro{\trackedmacro}{^^J==> #1 (\contextinfo)}}{}{}

into this

\xapptocmd\myproduct{\typeout{MacMessage: \contextinfo}%
  \protected@edef\tmp{\contextinfo}%
  \expandafter\addtotrackedmacro\expandafter{\tmp}{#1}}{}{}
\newcommand*\addtotrackedmacro[2]{\g@addto@macro\trackedmacro{^^J==> #2 (#1)}}

would solve that problem.


Another solution to solve that problem, is to use \edef rather than \def in place of \g@addto@macro, so you could change

\g@addto@macro{\trackedmacro}{^^J==> #1 (\contextinfo)}

to

\protected@xdef\trackedmacro{\unexpanded\expandafter{\trackedmacro}^^J==> #1 (\contextinfo)}

which would get the current value of \trackedmacro (expanding it once) and then add your code.

  • Thanks! Yes, I understood the problem although arguably not how to deliver it ideally to TeXnicians. I knew what I needed to do, just could not seem to do it. I actually experimented with \protected@edef\tmp{\contextinfo} but I was missing the next expansion of tmp before getting it into \tracketmacro. Thanks for taking the time to explain stuff. – Jonathan Komar Jul 7 '16 at 19:17
2

Try this, but I don't know whether this is the correct result.
In my opinion, the \tracklistmacro should be recursively defined within \section then.

\listfiles
\documentclass{article}
\usepackage{atveryend}
\usepackage{currfile}% adds \currfilepath

% Define a couple commands to track
\DeclareRobustCommand{\myapp}[1]{\textit{#1}}
\DeclareRobustCommand{\myproduct}[1]{MacGyver.\textit{#1}}
\makeatletter
\newcommand{\@myapp}[1]{#1}
\newcommand{\@myproduct}[1]{MacGyver.#1}
\makeatother

% The following is normally put into separate file as to modularize the application of patches
% Define a context macro as described
\def\trackedmacrolist{Tracked Macro List}% initialize macro
\AtEndAfterFileList{\typeout{\trackedmacrolist}}

\makeatletter
% Apply Patches
\usepackage{regexpatch}
\def\foo@@{}
\xapptocmd{\myproduct}{\g@addto@macro{\trackedmacrolist}{^^J==> #1 (\contextinfo)}}{}{}% <-- I'd rather show MacGyver.monkey shampoo, but unsure how best to implement
% Note: \typeout is always fully expanded due to \write %http://tex.stackexchange.com/a/60976/13552
\makeatother

%\usepackage{xparse}% Loaded by regexpatch
\let\oldsection\section
\makeatletter
\RenewDocumentCommand{\section}{s o m}{%
  \begingroup
  % Extract the counter representation
  \stepcounter{section}%
  \xdef\thenumber{\thesection}%
  \addtocounter{section}{-1}%
  % Temporarily change \myapp and \myproduct to be expandable and ordinary
  \let\myapp\@myapp
  \let\myproduct\@myproduct
  % Extract the title with possible \myapp and \myproduct (now ordinary/expandable)
  % Extract the title with possible \myapp and \myproduct (now ordinary/expandable)
  \protected@xdef\thetitle{#3}%
  \endgroup
  % Regular section
  % 
  \xdef\contextinfo{\thenumber:\thetitle:[\the\c@page]:\currfilepath:\the\inputlineno}%
  \IfBooleanTF{#1}
    {\oldsection*{#3}}
    {\IfValueTF{#2}
      {\oldsection[#2]{#3}}
      {\oldsection{#3}}}
   \xdef\trackedmacrolist{\trackedmacrolist}%
  }
\makeatother

\begin{document}
\section{About \myproduct{elephant shampoo}}
\meaning\trackedmacrolist
\clearpage
\section{About \myproduct{monkey shampoo}}

\clearpage
\section{About \myproduct{giraffe shampoo}}

\clearpage
\section{About \myproduct{tiger shampoo}}

\end{document}

Tracked Macro List ==> elephant shampoo (1:About MacGyver.elephant shampoo:[1]:macmadness.tex:56) ==> monkey shampoo (2:About MacGyver.monkey shampoo:[2]:macmadness.tex:59) ==> giraffe shampoo (3:About MacGyver.giraffe shampoo:[3]:macmadness.tex:62) ==> tiger shampoo (4:About MacGyver.tiger shampoo:[4]:macmadness.tex:65) )

  • Your output is one of: The first line shows monkey instead of elephant etc. Also your \xdef are imho a bit dangerous -- the title can contain more than simple ascii. – Ulrike Fischer Jul 7 '16 at 14:29
  • @UlrikeFischer: Yes, thanks, I shifted the recursive \xdef to the wrong position. It should be correct now – user31729 Jul 7 '16 at 14:32
  • @ChristianHupfer Interesting approach. I am not sure I like defining \contextinfo for each subsection, subsubsection, paragraph, etc. althought those others weren't in my example. Why did you use \the\c@page and def\foo@@{}? Sidenote: In my actual doc I use titlesec and \thetitle is reserved. I therefore use \currtitle and \currnum as global macros – Jonathan Komar Jul 7 '16 at 20:58
  • Actually you post a lot of questions that use different setups in the background such that it is hard to understand what you really want to achieve – user31729 Jul 7 '16 at 21:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.