6

Is there any easy way to adapt soulutf8 to work with LaTeX math mode delimiters?

As of now, compiling

\documentclass{article}
\usepackage{xcolor,soulutf8}
\begin{document}
Well, \hl{\(e^{i\pi}=-1\)}.
\end{document}

with pdflatex results in

! Extra }, or forgotten $.
\SOUL@doword ...hskip \z@ \relax \the \SOUL@word }
                                                  \let \SOUL@errmsg \SOUL@er...
l.4 Well, \hl{\(e^{i\pi}=-1\)}
                              .
? X

Let us not go into the discussion on which one is better: $...$ or \(...\); take it for granted that the typesetter has his/her reasons to prefer the second pair of delimiters.

P.S. As Heiko pointed out, the same problem exists in soul (which unfortunately seems to lack an active maintainer as of now), whose documentation specifically mentions this limitation.

  • @VasiliPupkin you can patch soul but only under the conditions of the LPPL (which clearly allows you to modify the package locally -- redistribution only under certain conditions) – Skillmon Apr 11 '18 at 19:49
  • @HeikoOberdiek Oh, I see now. There are two copyrights: one for the dtx file and another one for the sty file. Since there is no need to ship the sty file, its copyright can be effectively ignored. –  Apr 12 '18 at 17:36

3 Answers3

4

One could parse the argument of \hl to replace all \(...\) by $...$ in that argument and then use the original definition of \hl. I'm not entirely sure whether the following is completely stable, though it worked for everything I've thrown at it so far.

Edit: minimise code by removing case which is never matched (##3 is dot and ##2 is not) and not include the \(.\) tokens in every recursion of the replacement but only once (which suffices as those are in ##3 in the case of a recursion).

Note that there are cases of valid but strange syntax (if used outside of the argument of \hl) which fail here: If one mixes $ and one of \( or \) as the delimiters the provided code fails; though $a^b\) or \(a^b$ would be valid syntax \hl{$a^b\)} and \hl{\(a^b$} would fail as they don't match the required argument pattern of \hl@grab.

\documentclass{article}
\usepackage{xcolor,soulutf8}
\usepackage[]{amsmath}
\usepackage{letltxmacro}
\LetLtxMacro\hlBAK\hl
\begingroup
\makeatletter
\catcode`.=4% changing catcode so the dot is never matched by ordinary text
\def\zz%
  {\endgroup
    \renewcommand\hl[1]
      {%
        \hl@grab##1\(.\)\endhl@grab
      }%
    \long\def\hl@grab##1\(##2\)##3\endhl@grab%
      {%
        \hl@grab@ifdot{##2}
          {\hlBAK{##1}}
          {\hl@grab##1$##2$##3\endhl@grab}%
      }%
    \long\def\hl@grab@ifdot##1%
      {%
        \ifx.##1%
          \expandafter\@firstoftwo
        \else
          \expandafter\@secondoftwo
        \fi
      }%
  }%
\zz
\begin{document}
Well, \hl{\(.e^{i\pi}=-1\) text \(2=2\).}
\end{document}

enter image description here

EDIT: The following provides a replacement using two macros, one to replace \( and one to replace \). As a result the strange (but technically correct) syntax of \(a^b$ and $a^b\) should also work as an argument to \hl (though this way \hl{\(a^b\( text $a^b$} would work, too, which is obviously not correct syntax).

\documentclass{article}
\usepackage{xcolor,soulutf8}
\usepackage[]{amsmath}
\usepackage{letltxmacro}
\LetLtxMacro\hlBAK\hl
\begingroup
\makeatletter
\catcode`.=4% changing catcode so the dot is never matched by ordinary text
\def\zz%
  {\endgroup
    \renewcommand\hl[1]
      {%
        \hl@grab@A##1\(.\endhl@grab
      }%
    \long\def\hl@grab##1\(##2\)##3\endhl@grab%
      {%
        \hl@grab@ifdot{##2}
          {\hlBAK{##1}}
          {\hl@grab##1$##2$##3\endhl@grab}%
      }%
    \long\def\hl@grab@A##1\(##2\endhl@grab%\)
      {%
        \hl@grab@ifdot{##2}
          {\hl@grab@B##1\).\endhl@grab}
          {\hl@grab@A##1$##2\endhl@grab}%
      }%
    \long\def\hl@grab@B##1\)##2\endhl@grab%
      {%
        \hl@grab@ifdot{##2}
          {\hlBAK{##1}}
          {\hl@grab@B##1$##2\endhl@grab}%
      }%
    \long\def\hl@grab@ifdot##1%
      {%
        \ifx.##1%
          \expandafter\@firstoftwo
        \else
          \expandafter\@secondoftwo
        \fi
      }%
  }%
\zz
\begin{document}
Well, \hl{\(.e^{i\pi}=-1\) text \(2=2\).}

\hl{$a^b)}\hl{(a^b$}

\hl{(a (b$ $} \end{document}


Using etl as a faster, yet less versatile alternative to l3regex to also replace \( and \) inside of braces:

\documentclass{article}
\usepackage{xcolor,soul}
\usepackage{etl}
\NewCommandCopy\hlBAK\hl
\ExplSyntaxOn
\renewcommand\hl[1]
  {
    \exp_args:Ne \hlBAK
      {
        \etl_token_replace_all_deep:eNn
          { \etl_token_replace_all_deep:nNn {#1} \( { $ } }
          \) { $ }
      }
  }
\cs_generate_variant:Nn \etl_token_replace_all_deep:nNn { e }
\ExplSyntaxOff

\begin{document} \hl{\emph{Some important ( m = ath )}} \end{document}

Skillmon
  • 60,462
  • 1
    @user49915 the changes made by \makeatletter and \catcode\.=4are local to a group which is started by\bgroupand ended by\zzas the definition of\zzstarts with an\egroup. This is a neat trick to not alter any catcodes while still defining stuff with non standard catcodes. Per convention those should be\begingroupand\endgroup`, though, so you're right, the code is not perfect, I'll edit it. – Skillmon Jul 26 '18 at 12:58
  • 1
    @user49915 \bgroup is essentially the same as { (except that it doesn't have to be balanced while you define stuff), as a result \bgroup and \egroup can be balanced with { and }, while \begingroup must be balanced by \endgroup. It is convention (at least in l3) that you don't use \bgroup and \egroup for programming, so by that convention I should've used \begingroup and \endgroup. To see that \makeatletter has no effect anymore, you could place \@gobble{this} after \zz, it'll result in You can't use \\spacefactor' in vertical mode.` – Skillmon Jul 26 '18 at 13:46
  • I've just tested your code and got an error when trying \hl{\emph{\(…\)}}: running latex on https://pastebin.com/w5eDPm1J results in the error ! Extra }, or forgotten $. \SOUL@dosyllable ...x {\SOUL@tt \the \SOUL@token } \advance \SOUL@syllwidth \... to the console. Might I ask you to take a look? –  Jun 03 '23 at 23:34
  • @AlMa0 problem is that the \( and \) tokens are hidden inside the argument to \emph where this code doesn't see them. Either use $ directly or the l3regex variant provided by egreg (l3regex might be slower, but it will also find the tokens nested in groups). – Skillmon Jun 04 '23 at 10:35
  • 1
    @AlMa0 now there is also another alternative that uses etl instead of l3regex (this should be a bit faster than the regular expression based approach, though etl is less versatile than l3regex). – Skillmon Jun 04 '23 at 19:58
  • Works like a charm, thx! +1 –  Jun 05 '23 at 14:09
4

With the l3regex module it's quite easy and works for all soul commands. The idea is to redefine \SOUL@start, which is the macro that does all the business according to settings already made by the called macros.

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

\ExplSyntaxOn \tl_new:N __l_SOUL_argument_tl \cs_set_eq:Nc \SOUL_start:n { SOUL@start } \cs_generate_variant:Nn \SOUL_start:n { V } \cs_set_protected:cpn {SOUL@start} #1 { \tl_set:Nn __l_SOUL_argument_tl { #1 } \regex_replace_all:nnN { \c{(} (.*?) \c{)} } % look for (...) (lazily) { \cM$ \1 \cM$ } % replace with $...$ __l_SOUL_argument_tl \SOUL_start:V __l_SOUL_argument_tl % do the usual } \ExplSyntaxOff

\begin{document}

Well, \hl{(e^{i\pi}=-1) text (2=2).}

Well, \ul{(e^{i\pi}=-1) text (2=2).}

Well, \so{(e^{i\pi}=-1) text (2=2).}

Well, \caps{(e^{i\pi}=-1) text (2=2).}

Well, \st{(e^{i\pi}=-1) text (2=2).}

\end{document}

enter image description here

egreg
  • 1,121,712
3

The use of \( and \) is not supported by package soul, from the documentation:

§ 3 Mathematics:

Example: \so{foo$x^3$bar}

Mathematic formulas are allowed, as long as they are surrounded by $.

Note that the LaTeX equivalent \(...\) does not work.

Thus, the workaround with $ remains:

\hl{$e^{i\pi}=-1$}
Heiko Oberdiek
  • 271,626
  • 1
    Given that you've worked on integrating soulutf8 into soul recently, would it be possible for us to ask you to integrate also http://tex.stackexchange.com/a/48502 and http://tex.stackexchange.com/a/426036 (or equivalent means of solving the issues addressed by these answers) into soul.sty (unless this has already happened and is to appear soon in TeX Live, of course)? –  Apr 09 '23 at 18:27
  • @AlMa0 The integration work is done by David Carlisle and Ulrike Fischer AFAIK, I do not know the details. There is an issue tracker for bug reports and feature requests. – Heiko Oberdiek May 25 '23 at 22:55
  • Already asked: https://github.com/ho-tex/soul/issues/6 . –  May 25 '23 at 22:59