5

In the MWE below, the code:

\MyUl{abd \soulomit{\MyLink{www.google.com}{The Google}} ghi}

works exactly as desired:

enter image description here

However, when I attempt to wrap this in a macro, and use

\MyUl{abd \SoulOmitMyLink{www.google.com}{The Google} ghi}

I get:

Argument of \SoulOmitMyLink has an extra }.

Question:

What changes are required to \SoulOmitMyLink to get this to work?

Notes:

  • The following is not an option for my current use case:

    \MyUl{abd} \MyLink{www.google.com}{The Google}  \MyUl{ghi}
    

Code:

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\usepackage{soul}
\setulcolor{red}

\usepackage{hyperref}

\NewDocumentCommand{\MyUl}{%
    s%   #1 = starred variant
    O{}% #2 = options, if any
    m%   #3 = mandatory param
}{%
    \ul{#3}%
}%

\NewDocumentCommand{\MyLink}{%
    s%   #1 = starred variant  (unused as of yet)
    O{}% #2 = options, if any  (unused as of yet)
    m%   #3 = link text
    m%   #4 = link target
}{%
    \href{#3}{#4}%
}%

\NewDocumentCommand{\SoulOmitMyLink}{%
    s%   #1 = starred variant  (unused as of yet)
    O{}% #2 = options, if any  (unused as of yet)
    m%   #3 = link text
    m%   #4 = link target
}{%
    \IfBooleanTF{#1}{%
        \soulomit{\MyLink*[#2]{#3}{#4}}%
    }{%
        \soulomit{\MyLink[#2]{#3}{#4}}%
    }%
}%


\begin{document}
\MyUl{abd \soulomit{\MyLink{www.google.com}{The Google}} ghi}

%\MyUl{abd \SoulOmitMyLink{www.google.com}{The Google} ghi}% I want this
\end{document}
7

As explained by Joseph Wright in his answer, package soul does not support arbitrary commands. The following example shows, how soul can be extended to support \SoulOmitMyLink. It hooks into the code \SOUL@docmd that processes the data of \soulregister. The code is inserted for the case that the second argument of \soulregister is 8. Then the command in the first argument is also tested, whether it is \SoulOmitMyLink and the special treatment for this case is added.

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\usepackage{soul}
\setulcolor{red}

\usepackage[colorlinks]{hyperref}

\NewDocumentCommand{\MyUl}{%
    s%   #1 = starred variant
    O{}% #2 = options, if any
    m%   #3 = mandatory param
}{%
    \ul{#3}%
}%

\NewDocumentCommand{\MyLink}{%
    s%   #1 = starred variant  (unused as of yet)
    O{}% #2 = options, if any  (unused as of yet)
    m%   #3 = link text 
    m%   #4 = link target
}{%
    \href{#3}{#4}%
}%

\NewDocumentCommand{\SoulOmitMyLink}{%
    s%   #1 = starred variant  (unused as of yet) 
    O{}% #2 = options, if any  (unused as of yet)
    m%   #3 = link text
    m%   #4 = link target
}{%  
    \IfBooleanTF{#1}{%
        \soulomit{\MyLink*[#2]{#3}{#4}}%
    }{%
        \soulomit{\MyLink[#2]{#3}{#4}}%
    }%
}%

%%% Patch begin %%%
\usepackage{etoolbox}
\makeatletter
\patchcmd\SOUL@docmd{\else\ifx7}{%
  \ifx\SoulOmitMyLink#2%
    \SOUL@doword
    \let\SOUL@@\@undefined
    \NewDocumentCommand{\SOUL@@}{sO{}mm}{%
      \IfBooleanTF{##1}{%
        \soulomit{\SoulOmitMyLink*[##2]{##3}{##4}}%
      }{%
        \soulomit{\SoulOmitMyLink[##2]{##3}{##4}}%
      }%
      \SOUL@scan
    }%
  \fi
  \else\ifx7%
}{%
  \soulregister{\SoulOmitMyLink}{8}%
}{}
\makeatother
%%% Patch end %%%

\begin{document}
\MyUl{abd \soulomit{\MyLink{www.google.com}{The Google}} ghi}

\MyUl{abd \SoulOmitMyLink{www.google.com}{The Google} ghi}% I want this
\end{document}

Result

(I have used option colorlinks for hyperref to show in a clearer way that you do not want to have a line for the link.)

Update

  • According to Peter Grill's comment the macro \SoulOmitMyLink is no longer needed.
  • The redefinition of \SOUL@@ that scans the arguments for \MyLink do not need to check the arguments and call \MyLink. It can directly pass the arguments to \MyLink␣code, the internal macro that xparse defines, if a macro is defined via \NewDocumentCommand.

The updated and shorter example file:

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}
\usepackage{soul}
\setulcolor{red}

\usepackage[colorlinks]{hyperref}

\NewDocumentCommand{\MyUl}{%
    s%   #1 = starred variant
    O{}% #2 = options, if any
    m%   #3 = mandatory param
}{%
    \ul{#3}%
}%

\NewDocumentCommand{\MyLink}{%
    s%   #1 = starred variant  (unused as of yet)
    O{}% #2 = options, if any  (unused as of yet)
    m%   #3 = link text
    m%   #4 = link target
}{%
    \href{#3}{#4}%
}%

\usepackage{etoolbox}
\makeatletter
\patchcmd\SOUL@docmd{\else\ifx7}{%
  \ifx\MyLink#2%
    \SOUL@doword
    \let\SOUL@@\@undefined
    \NewDocumentCommand{\SOUL@@}{sO{}mm}{%
      \csname MyLink code\endcsname{##1}{##2}{##3}{##4}%
      \SOUL@scan
    }%
  \fi
  \else\ifx7%
}{%
  \soulregister{\MyLink}{8}%
}{}
\makeatother

\begin{document}
\tracingmacros=1
\MyUl{abd \soulomit{\MyLink{www.google.com}{The Google}} ghi}

\MyUl{abd \MyLink{www.google.com}{The Google} ghi}% I want this
\end{document}
|improve this answer|||||
  • Thanks, this works great. I just noticed that I can remove the call to \soulomit from the \SoulOmitMyLink macro. Hence, I can just eliminate \SoulOmitMyLink totally and simply use \MyLink. – Peter Grill Jun 22 '13 at 22:31
  • I don't understand how ##1 and ##2 work here? Perhaps there is some magic about the first parameter being a *, but how is the mandatory {##2} (mandatory since it is surrounded with curly braces) interpreted as the optional first parameter (which was expected to be within a square bracket)? I only saw that you had updated the solution as I am having trouble using two consecutive (without any text in between) uses of \MyLink within \MyUl. Your code here works great for that case, so I don't know what is going on with my actual use case so was wondering how this all worked. – Peter Grill Jun 23 '13 at 23:01
  • Ok, found my problem: my arg specs were not matching, so after fixing that now things work for the consecutive issue outlined above (your code does not have this problem). But, for me using {##1}{##2}{##3}{##4} did not work. But, using \IfBooleanTF{##1} as a switch and then passing either *[##2][##3]{##4} or [##2][##3]{##4} depending on the switch did work. – Peter Grill Jun 23 '13 at 23:19
5

The soul package works by doing a token-by-token decomposition of the input, which can be bypassed by \soulomit if it appears in the text to be decomposed. In your example, \soulomit does not appear in the input that soul sees. What happens is that soul extracts \SoulOmitMyLink and tries to use it with no argument, which is where the error comes in. Bottom line: you can't hide \soulomit inside another macro, as soul looks for that as a literal and not for it being used (it doesn't actually do anything).

|improve this answer|||||

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.