- If you know which classes/packages to search in, then the
latexdef
tool is a good choice.
- If you don't know, and your document loads lots of packages and/or is divided into subfiles that define macros, then Leo's
currfile
solution is helpful.
However, those solutions will only find the first definition, which may not help if the macro you are looking for is redefined with a different definition later.
Here I build on Leo's solution to report online (as in, on the terminal and pausing execution with \show
, rather than saving to another file) each file in which the definition of your target macro(s) gets changed, wrapped in a package reportmacrodefn.sty
:
\ProvidesPackage{reportmacrodefn}
\RequirePackage{currfile}
\def\reportmacrodefn#1{%
\ifdefined #1
\edef\foundmacrodefn{^^J^^J\space'\string #1' is already defined^^J\space\space\space\space (with meaning \meaning #1),^^J\space\space\space\space(i.e. it is defined before executing the '\string\reportmacrodefn' line, possibly earlier in the document, in the format file or as a primitive.)^^J\space\space\space\space Will look for re-definitions}%
\show\foundmacrodefn
\else
\typeout{** Looking for definitions of \string #1...}%
\fi
\expandafter\let\csname macrodefn\string #1\endcsname #1%
\AtBeginOfFiles{%
\expandafter\let\csname\currfilename/ macrodefn\string #1\endcsname #1%
}%
\AtEndOfFiles{%
\begingroup\expandafter\endgroup\expandafter\let\expandafter\reportmacrodefntmp\csname\currfilename/ macrodefn\string #1\endcsname% avoid \relax side-effect of \csname...\endcsname
\unless\ifx\reportmacrodefntmp #1% check if the macro has changed within file \currfilename
\begingroup\expandafter\endgroup\expandafter\let\expandafter\reportmacrodefntmpreported\csname macrodefn\string #1\endcsname
\unless\ifx\reportmacrodefntmpreported #1%check if the macro has changed since the last time we reported it
% don't report a line number as this only gets executed at the end of the input file so would be useless and misleading
\edef\foundmacrodefn{^^J^^J\space'\string #1' changed^^J\space\space\space\space in '\currfilename' (file stack: \currfiledumpstack), ^^J\space\space\space\space was \meaning\reportmacrodefntmp^^J\space\space\space\space now \meaning #1}%
\show\foundmacrodefn
\expandafter\let\csname macrodefn\string #1\endcsname #1%
\fi
\fi
}}
% currfile doesn't provide a friendly way to dump the stack, so here's one:
\ifcurrfile@abspath
\newcommand*\currfiledumpstack{%
\expandafter\currfile@dumpstack\currfile@stack\stopquark\stopquark\stopquark\stopquark
}
\def\currfile@dumpstack#1#2#3#4{%
\ifx#1\stopquark\@empty
\else
{#2\ifx\empty#3\empty\else.#3\fi}%
\expandafter\currfile@dumpstack
\fi
}
\else
\newcommand*\currfiledumpstack{%
\expandafter\currfile@dumpstack\currfile@stack\stopquark\stopquark\stopquark
}
\def\currfile@dumpstack#1#2#3{%
\ifx#1\stopquark\@empty
\else
{#2\ifx\empty#3\empty\else.#3\fi}%
\expandafter\currfile@dumpstack
\fi
}
\fi
Here's a test document to illustrate how to use the package (I called it reportmacrodefntest.tex
):
\RequirePackage{reportmacrodefn}
\reportmacrodefn\[
\reportmacrodefn\text
\documentclass{article}
\usepackage{fixltx2e}
\usepackage{amsmath}
\begin{document}
See the terminal output during compilation, or the log file.
\end{document}
And the relevant output here is:
'\[' is already defined
(with meaning macro:->\relax \ifmmode \@badmath \else \ifvmode \nointerlineskip \makebox [.6\linewidth ]{}\fi $$\fi ),
(i.e. it is defined before executing the '\reportmacrodefn' line, possibly earlier in the document, in the format file or as a primitive.)
Will look for re-definitions.
'\[' changed
in 'fixltx2e.sty' (file stack: {reportmacrodefntest.tex}{}),
was macro:->\relax \ifmmode \@badmath \else \ifvmode \nointerlineskip \makebox [.6\linewidth ]{}\fi $$\fi
now macro:->\x@protect \[\protect \[ .
'\text' changed
in 'amstext.sty' (file stack: {amsmath.sty}{reportmacrodefntest.tex}{}),
was undefined
now macro:->\protect \text .
A couple of features of this solution that are worthy of note:
- Both
fixltx2e
and amsmath
provide the same (one-level) definition of \[
, and only the first one is reported.
- The
\text
macro is defined in amstext.sty
, which was loaded by amsmath
, and the definition is only reported once in the correct file, and the file stack is summarised too.
Of course, one still has to resort to grep
or other approaches to find out where in the named file(s) the definition(s) are.
If the definition you are looking for is in the format file, then you should look in latex.ltx
etc as suggested by Herbert, and the --source
option to latexdef
may be helpful.
Finally, this doesn't help if you're looking for something defined by the reporting code!
\def
s to log them appropriately to a Lua table. You could then rebuild Latex, tex4ht-style, so that all macros have this information. I'm curious to see if I can do this: I've not really got the hang of token processing in Luatex yet, and it seems like a suitable challenge.\def
,\edef
,\xdef
,\gdef
starting with\def\dummy{}
to get the prefixes; then grab function and argument with\grabfargs
defined as\def\grabfargs#1#2#{...}
; then the replacement text and do the definition, logging whatever you want. Problems: this loses\afterassignment
(we can replace by storing with\let
), and treats\global\def
as\def
, so we need to redefine\global
as something like\@globaltrue\afterassignment\afterglobal
with suitable definitions. Not infeasible.