9

I am trying to make characters active in math mode, but can't seem to get past the most basic of examples. I would like to define a macro which is passed:

  1. the character to make active and

  2. the macro that should be executed once that macro is encountered.

The MNWE below attempts to make =, + and ! active and color code them. The desired output is that those characters get colored in math mode as. Thus, the input

a=+b $c=+4+d+3!$ e=+f

yields:

enter image description here

where the characters are not altered outside of math mode (the leading a=+b and trailing e=+f).

Notes:

  • There is a separate, but related (as it is in the context of active characters) question in the \MyPlus macro to detect a binary addition + versus a unary positive +. Perhaps that should be a separate question?

Related Questions:

Code (non-working):

\documentclass{article}
\usepackage{mathtools}
\usepackage{xparse}
\usepackage{xcolor}

\NewDocumentCommand{\MakeActiveChar}{%
    m% char to make active
    m% code to execute for this character
}{%
    \catcode`\#1=13\relax%
    \def#1{#2}%
}%

\let\OldEqual=
\newcommand*{\MyEqual}[1][green]{\mathrel{\textcolor{#1}{\OldEqual}}}%

\let\OldPlus+
\newcommand*{\MyPlus}[1][blue]{%
    %\catcode`\+=12% reset catcode so we can use it (not needed with \OldPlus?)
    %% Perhaps this if-then construct should be a separate question?
    %% if binary addition operator
        \mathbin{\textcolor{#1}{\OldPlus}}%
    %% if unary positive operator
    %%  \mathrel{\textcolor{red}{\OldPlus}}%
}%

\let\OldFactorial!
\newcommand*{\MyFactorial}{\textcolor{brown}{\OldFactorial}}%

\everymath{%
    \MakeActiveChar{=}{\MyEqual}%
    \MakeActiveChar{+}{\MyPlus}%
    \MakeActiveChar{!}{\MyFactorial}%
}%

\begin{document}
The ``math'' outside here is just to test that characters are active \emph{only} in math mode:

a=+b $c=+4+d+3!$ e=+f 
\end{document}
Peter Grill
  • 223,288

3 Answers3

10

You have to be careful with the declarations if you load amsmath; in the body of the definition, use \std{<char>} where you need the old meaning.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xcolor,etoolbox}

\makeatletter
\newcommand{\DeclareMathActive}[2]{%
  % #1 is the character, #2 is the definition
  \expandafter\edef\csname keep@#1@code\endcsname{\mathchar\the\mathcode`#1 }
  \begingroup\lccode`~=`#1\relax
  \lowercase{\endgroup\def~}{#2}%
  \AtBeginDocument{\mathcode`#1="8000 }%
}

\newcommand{\std}[1]{\csname keep@#1@code\endcsname}
\patchcmd{\newmcodes@}{\mathcode`\-\relax}{\std@minuscode\relax}{}{\ddt}
\AtBeginDocument{\edef\std@minuscode{\the\mathcode`-}}
\makeatother


\DeclareMathActive{+}{\mathbin{\textcolor{blue}{\std{+}}}}
\DeclareMathActive{!}{\mathclose{\textcolor{brown}{\std{!}}}}
\DeclareMathActive{-}{\mathbin{\textcolor{red}{\hat{\std{-}}}}}

\begin{document}

a=+b $c=+4+d+3!-1$ e=+f

\end{document}

Don't do \let\OldPlus=+.

enter image description here

With Unicode engines (xelatex or lualatex), the patch must be done differently. Here's a version that copes with all engines.

\documentclass{article}
\usepackage{amsmath}
\usepackage{xcolor,etoolbox}
\usepackage{iftex}

\makeatletter
\newcommand{\DeclareMathActive}[2]{%
  % #1 is the character, #2 is the definition
  \expandafter\edef\csname keep@#1@code\endcsname{\mathchar\the\mathcode`#1 }
  \begingroup\lccode`~=`#1\relax
  \lowercase{\endgroup\def~}{#2}%
  \AtBeginDocument{\mathcode`#1="8000 }%
}

\newcommand{\std}[1]{\csname keep@#1@code\endcsname}
\iftutex
  \patchcmd{\newmcodes@}{\Umathcodenum `\-\relax}{\std@minuscode\relax}{}{\ddt}
\else
  \patchcmd{\newmcodes@}{\mathcode`\-\relax}{\std@minuscode\relax}{}{\ddt}
\fi
\AtBeginDocument{\edef\std@minuscode{\the\mathcode`-}}
\makeatother


\DeclareMathActive{+}{\mathbin{\textcolor{blue}{\std{+}}}}
\DeclareMathActive{!}{\mathclose{\textcolor{brown}{\std{!}}}}
\DeclareMathActive{-}{\mathbin{\textcolor{red}{\hat{\std{-}}}}}

\begin{document}

a=+b $c=+4+d+3!-1$ e=+f

\end{document}
egreg
  • 1,121,712
  • Why does the - require special handling? Are there other characters that require special processing? – Peter Grill Mar 19 '16 at 11:01
  • @PeterGrill I made a better fix. – egreg Mar 19 '16 at 11:14
  • You surely know that \expandafter\mathchardef\csname keep@#1@code\endcsname=\mathcode`#1 does similar work like your \edef. – wipet Mar 19 '16 at 13:43
  • @wipet Yes, thanks for reminding, though. – egreg Mar 19 '16 at 13:45
  • There is a bug in \DeclareMathActive: it should use two space tokens in the replacement text of \csname keep@#1@code\endcsname. eg try $\std{t}1$ with \DeclareMathActive{t}{U}. (or $\std{+}1$ in the context of your answer; I came here from another question which was linking to it) (you could put a \relax at the end of \std definition) –  May 13 '16 at 09:00
  • 1
    Due to \newmcodes@ having been recently changed in amsopn.sty to better behave with Unicode engines, this (the patch) won't work with XeLaTeX/LuaLaTeX. –  May 13 '16 at 09:08
  • Is there a way to make this work in XeLaTeX? – Bolpat Apr 15 '20 at 22:00
  • 1
    @Bolpat Added the modified version. – egreg Apr 15 '20 at 22:15
4

You can define \mathdef declarator and then use it:

\input opmac \localcolor

\def\mathdef#1{\mathcode`#1="8000 \bgroup \lccode`~=`#1\lowercase{\egroup\def~}}

\mathdef +{\mathbin{\Red\mathchar`+}}
\mathdef ={\mathrel{\Green\mathchar`=}}

aha + $a+b+c=d$

\bye

The \input opmac is here only for setting colors \Red, \Green etc. If you are using another macro for colors then use only the second line \def\mathdef... in your macros.

wipet
  • 74,238
  • 1
    You surely know that \mathord{\mathchar`.} and \mathpunct{\mathchar`,} are different from . and , in math mode. Also \mathbin{\mathchar`-} is quite different from -. Period and comma are taken from the math letters font, whereas the minus is taken from the symbol font. – egreg Mar 19 '16 at 10:49
  • When I attempted to use this I got Illegal parameter number in definition of \mathdef. – Peter Grill Mar 19 '16 at 11:03
3

There are a few problems:

\MakeActiveChar does \def#1{#2} which is not permitted since #1 is a character that is not active. Here's an alternative with The \lowercase trick

\NewDocumentCommand\MakeActiveChar{mm}
  {\begingroup\lccode`\~=`#1\lowercase{\endgroup\def~}{#2}%
   \catcode`#1=12 \mathcode`#1="8000 }

Apart from that, you have a missing = in a \let. The first equal is optional, so \let\a=\b is correct, the equal, however, is necessary when you want to let to = because if not, it would be treated as the optional one, so

\let\originalequal=

should be

\let\originalequal==

And the last part is that by making a character active in math mode, and then doing \newcommand\plus{\mathbin{\originalplus}} you get an endless loop like \def\a{\a}, so you need to use for instance \mathchar`\+ or \mathchar`+ instead.

Complete solution

\documentclass{article}
\usepackage{mathtools}
\usepackage{xparse}
\usepackage{xcolor}

\NewDocumentCommand\MakeActiveChar{mm}
  {\begingroup\lccode`\~=`#1\lowercase{\endgroup\def~}{#2}%
   \catcode`#1=12 \mathcode`#1="8000 }

\newcommand*\mathcolor[2]{\begingroup\color{#1}#2\endgroup}

\newcommand*\equal[1][green]{\mathrel{\mathcolor{#1}{\mathchar`\=}}}
\newcommand*\plus[1][blue]{\mathbin{\mathcolor{#1}{\mathchar`\+}}}
\newcommand*\factorial{\mathord{\mathcolor{brown}{\mathchar`\!}}}

\everymath{%
  \MakeActiveChar{=}{\equal}%
  \MakeActiveChar{+}{\plus}%
  \MakeActiveChar{!}{\factorial}%
}

\begin{document}
The ``math'' outside here is just to test that characters are active \emph{only} in math mode:

a=+b $c=+4+d+3!$ e=+f 
\end{document}

By the way, I defined \mathcolor because \textcolor works with \bgroup/\egroup which create an ord atom.

Manuel
  • 27,118
  • Can you post a compilable MWE? I get Missing \begin{document} when I replace my definition of \MakeActiveChar with yours. Also, can you explain why you suggest the double equal? If I use the double equal as you suggest, along with your ``\MakeActiveChar, I getTeX capacity exceeded, sorry [grouping levels=255]`. – Peter Grill Mar 19 '16 at 10:26
  • Okey, sorry, I just did it without trying, and the error comes from somewhere else. I'll edit. – Manuel Mar 19 '16 at 10:28
  • Still not a compilable MWE. And again I get LaTeX Error: Missing \begin{document} when I replace your \MakeActiveChar in the MWE I posted in the question. – Peter Grill Mar 19 '16 at 10:52
  • 1
    Sorry about the missing compilable example. The point about \std@minus in egreg answer is interesting, don't miss it. – Manuel Mar 19 '16 at 10:54