MWE:
\documentclass{minimal}
\usepackage{commath, tikz}
\begin{document}
\begin{tikzpicture}
\node {$:$};
\end{tikzpicture}
\end{document}
When I compile this, pdfTeX enters an infinite loop. Why?
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It only takes a minute to sign up.
Sign up to join this communityThe problem is that TikZ overrides the definition of the active colon given by commath
(that I don't recommend using).
This should do:
\documentclass{minimal}
\usepackage{commath,tikz}
\makeatletter
\protected\def\tikz@nonactivecolon{\ifmmode\mathrel{\mathop\ordinarycolon}\else:\fi}
\makeatother
\begin{document}
\begin{tikzpicture}
\node {$:$};
\end{tikzpicture}
\end{document}
Note that the same would happen if mathtools
is loaded and
\mathtoolsset{centercolon}
is used. But the same workaround works.
Here is a version that works independently of the loaded packages, assuming of course that tikz
is loaded.
\documentclass{article}
\usepackage[french]{babel}
\usepackage{mathtools}\mathtoolsset{centercolon}
%\usepackage{commath}
\usepackage{tikz}
\usepackage{etoolbox}
\makeatletter
\AtEndPreamble{
\ifnum\mathcode`\:=\string"8000
\begingroup\lccode`\~=`\:
\lowercase{\endgroup\let\math@colon@meaning~}
\else
\expandafter\let\expandafter\math@colon@meaning\string:
\fi
}
\AtBeginDocument{
\ifnum\catcode`\:=\active
\letcs\text@colon@meaning{active@char\string:}
\else
\expandafter\let\expandafter\text@colon@meaning\string:
\fi
\protected\def\tikz@nonactivecolon{%
\ifmmode
\expandafter\math@colon@meaning
\else
\expandafter\text@colon@meaning
\fi}
\begingroup\lccode`\~=`\:
\lowercase{\endgroup\let~\tikz@nonactivecolon}
}
\makeatother
\begin{document}
French: colon
\begin{tikzpicture}
\node {$:=$};
\end{tikzpicture}
\end{document}
tikz
robust regarding active !?;:
is really a pain. I tried to implement some improvements in recent cvs version but it seems that I overlooked active math chars :(
. Your edited code is really interesting: I'll probably add a fix inspired by it at least in the LaTeX
version of tikz
(but I cannot use etoolbox
). Could you add some comments to your code? TIA.
:
as active character using in text mode the babel
meaning and in math mode it does whatever has been setup before \AtBeginDocument
by commath
or mathtools
(or emitting a simple colon if its mathcode is not "8000).
\begin{equation}\label{eq:1}a=b\end{equation}
and then \begin{equation}\ref{eq:1}c=d\end{equation}
and you will have a surprise ... (don't do this at home!). Your patch doesn't catch that one but of course, without it it is much worse as the innocent $:$
when babel
is loaded with options french
and mathtools
does its centercolon
creates an infinite loop!
babel+french
alone $\ref{eq:1}$
is not a problem because babel
has patched \ref
in order for characters made active by babel
to be (supposedly) safe acting as \string<char>
. But if <char>
is mathematically active as well as being catcode active, this means that a loop is created! this happens with mathtools
when it makes :
mathematically active. This escapes the fixing done by your patch.
mathtools
alone because inside a \csname..\endcsname
a mathematically active character which is not catcode active is not
expanded.
I made so far three five edits to my initial answer:
I added some code to solve the problems originating with Babel when it has made a character (here the colon) active and some other package has made it also mathematically active; I wanted to make sure \label
and \ref
could be used in all situations (including language switches) and this is difficult.
In that first edit I had also basically copied over from egreg
the patch to \tikz@nonactivecolon
. In my second edit I elaborate more on this TikZ patch, because also there I wanted the \label
and \ref
to be usable inside tikz picture with the potentially active + mathematically active colon.
I came back with the idea that I could simplify my Babel patch but then I realized that there was a missing \fi
in it; then it became very confusing because TeX
complained when I added it, and I realized it had not associated correctly some \else
with the corresponding \if...
..[update: in fact I had done an error of coding originating in a (persistent) mis-understanding I have been plagued with of what TeX
does exactly when scanning conditionals. I have learned my lesson since ] ... I have moved all the stuff inside a dedicated macro which is defined first ... [and this was indeed a correct fix to this implementation problem].
Ok, so I have indeed a shorter version of \dotheactivecatcodesubroutine
which I append at the very bottom of this answer. I do not attempt an explanation, as it gives me a headache. It seems to work fine. I leave the original in place. To study the effects of either of them one can add things to the document body such as
{\ttfamily
\expandafter\meaning\csname user@active\string:\endcsname
\expandafter\meaning\csname normal@char\string:\endcsname}
to see depending on whether mathtools
is loaded or not what the patch
does to the Babel things.
I add at the end of this answer a slight variant of the TikZ
patch for the case of a (Babel) catcode active and simultaneously mathcode active colon. This uses \bbl@deactivate
. This seems to work also, and has the advantage or disadvantage that the colon now acts as a French (or other language making it active) colon inside the node
(and outside of math mode) of the tikz
picture.
It is a bit harsh on commath
to say that it is poorly written, when the problem arises because tikz
does not check for the mathematical activation of the colon!
Babel has exactly the same problem. Try
\documentclass{article}
\usepackage[french]{babel}
\mathcode`:="8000
{\catcode`:\active \gdef:{\ifmmode A\else B\fi}}
\begin{document}
a:b
\end{document}
So far so good. At begin document, the colon was made active by frenchb.sty
via the services of babel
. You can see the extra space before the colon.
Then try adding $a:b$
to the code: it falls into in an infinite loop. Why? because babel+frenchb
define the active colon to expand to some space followed with \string:
, that is the colon with type other
(category code 12), which is supposedly safe.
But it is not
necessarily safe in math mode! because we can have then the character to have been declared mathematically active, which means that although not being necessarily of category code 13 it still behaves (mostly) as if it were. So the \string:
which appears very safe is NOT! try this
\documentclass{article}
\mathcode`:="8000
\catcode`:\active \gdef:{\ifmmode MATH\else TEXT\fi}
\begin{document}
: % active colon gives TEXT
$:$ % active colon gives MATH
\string: % category 12 colon gives ... colon
$\string:$ % prints MATH! this shows that it was treated as being active!
\end{document}
Now commath
can do all the definitions you want of its math active colon, none of them will solve the problem! the definition done by commath
has nothing to do with the infinite loop! it is purely the fact that the colon is mathematically active that results in the infinite loop because it was thought (by tikz
or by frenchb+babel
) that a category 12 character is safe.
And indeed the patch by egreg
is applied to a macro of tikz
not to a macro of commath
!
I have worked hard to provide a patch to Babel+math active characters, and also TikZ (which was much easier). I am posting it although some problems will very probably remain (I am quite tired now and worked exponentially slower and slower until late at night...). I should say I spent all of my effort on Babel, and regarding TikZ I just looked very quickly. So I might have overlooked many things (especially so as I have never used TikZ.) There are a number of difficult things with Babel but the thorniest was when a character (here the colon) is mathematically active, and was catcode active but then the user switches languages and the character is normal in the new language (it remains catcode active though). The solution had to be compatible with using \label
and \ref
in math mode. Babel is very elaborate and careful when dealing with active characters but does not pay attention to math mode at all. (except for things such as the caret or the prime). (I said it was late so perhaps my last sentence is too quick and superficial)
I do not use etoolbox
.
\documentclass[fleqn]{article}
\usepackage[english,french]{babel} % can be commented out
\usepackage{mathtools}\mathtoolsset{centercolon} % can be commented out
\usepackage{tikz}
% this goes BEFORE \begin{document} and AFTER all packages
% such as mathtools which may let : be mathematically active
% (hopefully they do it already in the preamble).
\makeatletter
\ifnum\mathcode`\:=\string"8000
\begingroup\lccode`\~=`\: % and the tilde ~ must be active here.
\lowercase{\endgroup
\let\mathactive@colon@meaning~}
\fi
\makeatother
\makeatletter
% The next macro will be used at begin document (so after Babel
% has done its activation of characters, depending on the language)
% I had to move it here because of absolutely uncomprehensible
% problems with the parsing of the conditionals
% (adding braces changed nothing, I used `tracingcommands2` to
% confirm that TeX was taking the wrong `\else` but so far I have
% not understood why.)
% If Babel is not loaded what is done here does no harm.
% But it does no good either.
%
% Note: recent versions of frenchb.ldf do not use active characters
% under XeTeX but \XeTeXinterchartoks, which are no-op in math
% mode
%
% The characters activated by Babel do not check for math mode (except
% possibly the caret and the prime and maybe a few others, I should
% check in babel.pdf), it is up to the language definition
% file to do so. For example the
% extra spacing in front of !?;: is put by `frenchb`
% only in horizontal mode, not
% in math mode. In math mode the colon is just a standard category 12
% colon.
%
% But this creates an infinite loop is the colon is also
% mathematically active!
%
% The following cures this. It will let the
% colon act either as the standard category 12 colon or, if
% mathtools or another package has made the colon mathematically
% active (in the preamble) it will then use that definition.
%
% Let's store the Babel active and non-active versions,
% [nota bene: the ``normal char'' is the meaning to which the
% active character expands when the user switches languages to one
% where the character is not activated. The character remains
% active in the sense of catcode throughout the document except if
% the user uses \shorthandoff which really sets the catcode to 12.
% The following should be compatible with language changes. ]
%
% Babel uses \active@char: to let \user@active: become
% \normal@char: when it is used inside \ref, \newlabel, \bibcite...
% so to benefit from this we store the current
% meaning of \user@active:, and we do not modify \active@char:.
%
% The Babel definition of the active character
% puts a prefix in front of \active@char: (or \normal@char:) which
% results in first expansion of the active character :
% to be itself preceded
% by a \protect or \noexpand, or sometimes \active@char:. (so\label{eq:1}
% works ).
%
% On the other hand $\ref{eq:1}$ works for another reason:
% the \active@char: then expands to be \normal@char: hence
% to a category code 12 :. The mathcode is not a problem because this
% is inside \csname...\endcsname.
%
% If the user has switched to a language where the colon is
% `normal', the character remains active but when not
% protected expands to \normal@char: hence a category 12 :. And
% we are then doomed if the mathcode is 32768. Infinite loop.
%
% As \ref{eq:1} will make use of \normal@char: which then must expand
% to a category 12 colon, our possibilities to modify \normal@char:
% are greatly limited. As Babel has patched \ref and similar macros
% to set the \if@safe@actives flag to be true, we can test for it.
% We thus leave \normal@char: untouched except if \if@safe@actives if
% false (this takes care of \ref) and if \protect is
% \@typeset@protect: and only then we let \normal@char: use
% \mathactive@colon@meaning.
%
\def\dotheactivecatcodesubroutine{%
\expandafter\let\expandafter\original@user@active@colon
\csname user@active\string:\endcsname
\expandafter\let\expandafter\original@normal@char@colon
\csname normal@char\string:\endcsname
% should just be : with catcode 12
\ifnum\mathcode`\:=\string"8000
\expandafter\def\csname user@active\string:\endcsname{%
\ifmmode
\expandafter\mathactive@colon@meaning
\else
\expandafter\original@user@active@colon
\fi}
\def\hip@hop##1##2##3##4##5{##2##3##4##1}
\expandafter\def\csname normal@char\string:\endcsname{%
\ifmmode
\if@safe@actives\else
\ifx\protect\@typeset@protect % <-- this is probably superfluous
\hip@hop\mathactive@colon@meaning
\fi
\fi
\fi
\original@normal@char@colon}
\else
% here we have no problem of mathcode, but we would also like to
% annihilate (in a reasonable manner) the effects in math
% mode of the Babel activation of the character (here the colon).
\expandafter\def\csname user@active\string:\endcsname{%
\ifmmode
\expandafter\original@normal@char@colon
\else
\expandafter\original@user@active@colon
\fi}
\fi}
\AtBeginDocument{
\ifnum\catcode`\:=\active % We assume it is Babel+frenchb which is
% responsible for this
\dotheactivecatcodesubroutine
\fi
%
% if the character is not active, I will assume it will not be
% activated later by Babel (I will have to check the
% documentation). But we still have some problems
% with TikZ.... Indeed it seems that tikz
% `deactivates' the colon by re-defining its active version to
% expand to the category 12 colon:
% \def:{\tikz@nonactivecolon}% see tikz.code.tex 1521--1525&1439
% In the process the mathtools definition is thus overwritten
% but the mathcode is left to 32768... so the colon in math
% expands exactly as if it was active hence to
% \tikz@nonactivecolon which expands to the category 12 colon
% but don't forget the mathcode.... INFINITE LOOP.
\ifnum\mathcode`:=\string"8000
% if the colon is not mathematically active we don't have
% to do anything additional for TikZ now.
\@ifpackageloaded{tikz}
{%
% We have to distinguish the case of a catcode active colon.
% We are in that case in deep trouble
% Because TikZ does not try to modify the catcode (perhaps
% impossible due to argument parsing, I don't know) but
% redefines the active colon, we have to make sure this definition
% is safe with the math active colon. For this we simulate a
% switch to a language where the colon is `normal' in the sense
% of Babel, because we already solved the problem there. So we
% import that solution (of course this is assuming that the
% colon is active because of Babel in the first place).
%
% The following was done to make sure \label and \ref were usable
% inside tikz pictures, but perhaps this is not licit practice?
% (I don't know TikZ)
\ifnum\catcode`:=\active
\begingroup\lccode`~=`:
\lowercase{\endgroup
\let\original@babelactivecolon~
\g@addto@macro\tikz@deactivatthings{%
\expandafter\expandafter\expandafter\let
\expandafter\expandafter
\csname active@char\string:\endcsname
\csname normal@char\string:\endcsname
\def~{\original@babelactivecolon}}}
\else % colon is not catcode active
% in this branch the active colon will only be invoked in math mode
% because of the mathcode
% as the mathcode is not looked at in an \edef, \write, or a
% \csname.. \endcsname, I think I don't need the e-TeX \protected
\let\original@tikz@nonactivecolon\tikz@nonactivecolon
\def\tikz@nonactivecolon{%
\ifmmode
\expandafter\mathactive@colon@meaning
\else
\expandafter\original@tikz@nonactivecolon
\fi}
\fi}{}
\fi % end of TikZ patching for colon with mathcode 32768
} % end of AtBeginDocument
\makeatother
\begin{document}
\newcounter{testcounter}
\makeatletter
\@ifundefined{shorthandon}{}{French}%
\makeatother
: colon (:)
Math $:= (:)$ colon
Tikzpicture
\begin{tikzpicture}
\node {\refstepcounter{testcounter} a:b $ \label{pic:1} a:b $};
\end{tikzpicture}
\begin{tikzpicture}
\node {\refstepcounter{testcounter} c:d $ \label{pic:2} a:b \ref{pic:1}$};
\end{tikzpicture}
\begin{equation}
\label{eq:1}
a := b
\end{equation}
\begin{equation}
\label{eq:2}
b := a\quad (\mbox{see equation}\ \ref{eq:1})
\end{equation}
At the end of this manuscript we will have established equation \ref{eq:2}
starting from equation \ref{eq:1}.
\makeatletter
\@ifundefined{shorthandon}{}{\shorthandoff{:}not French anymore}%
\makeatother
: colon (:)
Math $:= (:)$ colon
Tikzpicture
\begin{tikzpicture}
\node {\refstepcounter{testcounter} d:e $ \label{pic:3} a:b \ref{pic:2}$};
\end{tikzpicture}
\begin{tikzpicture}
\node {\refstepcounter{testcounter} f:g $ \label{pic:4} a:b \ref{pic:3}$};
\end{tikzpicture}
\begin{equation}
\label{eq:3}
c := a\quad (\mbox{to be deduced from}\ \ref{eq:2})
\end{equation}
\makeatletter
\@ifundefined{shorthandon}{}{\shorthandon{:}}
\@ifundefined{shorthandon}{}{\selectlanguage{english}English}%
\makeatother
: colon (:)
Math $:= (:)$ colon
Tikzpicture
\begin{tikzpicture}
\node {\refstepcounter{testcounter} k:l $ \label{pic:5} a:b \ref{pic:4}$};
\end{tikzpicture}
\begin{tikzpicture}
\node {\refstepcounter{testcounter} m:n $ a:b \ref{pic:5}$};
\end{tikzpicture}
\begin{equation}
\label{eq:4}
c := b\quad (\mbox{to be deduced from}\ \ref{eq:3})
\end{equation}
\end{document}
Alternative version of \dotheactivecatcodesubroutine
:
\def\dotheactivecatcodesubroutine{%
\expandafter\let\expandafter\original@user@active@colon
\csname user@active\string:\endcsname
\expandafter\let\expandafter\original@normal@char@colon
\csname normal@char\string:\endcsname
% should just be : with catcode 12
\expandafter\expandafter\expandafter
\def\expandafter\expandafter
\csname user@active\string:\endcsname
\expandafter{\expandafter
\ifmmode
\expandafter\expandafter
\csname normal@char\string:\endcsname
\else
\expandafter\original@user@active@colon
\fi}
\def\hip@hop##1##2##3##4##5{##2##3##4##1}
\ifnum\mathcode`\:=\string"8000
\expandafter\def\csname normal@char\string:\endcsname{%
\ifmmode
\if@safe@actives\else
\ifx\protect\@typeset@protect % <-- this is probably superfluous
\hip@hop\mathactive@colon@meaning
\fi
\fi
\fi
\original@normal@char@colon}
\fi
}
That version of the TikZ
patch is simpler. Its effect seems to be that it avoids errors as well as the other one, but this time the colon acts as French colon inside the node
. I have not gone far into tikz.code.tex
but it seems it does \tikz@installcommands
and then \tikz@uninstallcommands
which has the effect to re-establish the initial definition of the active colon. The infinite loop is avoided.
\@ifpackageloaded{tikz}
{%
\ifnum\catcode`:=\active
\begingroup\lccode`~=`:
\lowercase{\endgroup
\g@addto@macro\tikz@deactivatthings{\bbl@deactivate~}}%
\else
\let\original@tikz@nonactivecolon\tikz@nonactivecolon
\def\tikz@nonactivecolon{%
\ifmmode
\expandafter\mathactive@colon@meaning
\else
\expandafter\original@tikz@nonactivecolon
\fi}%
\fi}{}
commath
doesn't come from this particular point (which is indeed TikZ fault, because it should check at begin document if a definition for the active colon exists). The macros in the package are poorly written: just look at the definition of \pd
.
\[\pd{f}{x}\left(\pd{f}{x}\right)\]
and you'll be quite surprised.
I spent hours debugging this. As @egreg already noted in his comment, commath
is a poorly written package. It attempts to fix the alignment of := this way:
\mathchardef\ordinarycolon\mathcode`\:
\mathcode`\:=\string"8000
\begingroup \catcode`\:=\active
\gdef:{\mathrel{\mathop\ordinarycolon}}
\endgroup
Unfortunately this hack is not quite perfect and causes an infinite loop in a TikZ node. I don't know the exact explanation of the error, but you're better off copying the command definitions from commath.sty to your preamble, or creating a local copy of the .sty file and removing these lines.
Does anyone know of a similar hack to fix := which works in a TikZ node too?
:
to be math active, preferring a personal command, such as \newcommand{\coloneq}{\mathrel{\mathop:}=}