Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I have a macro \command to ensure that the names of macros in my documentation are consistently formatted. Been work great, until I had an urge to procrastinate and had this brilliant idea that I can easily catch most spelling errors of the macro names, by checking that the macro exists.

Seems simple enough. So I added:

\ifcsname#1\endcsname%
\else%
    \par\textbf{\textcolor{red}{\textbackslash#1} is not defined.}
\fi%

to the macro that does the formating, where #1 is the name of the macro. This seems to work fine in the basic case as illustrated by the MWE below that produces:

enter image description here

and correctly tells me that bfseriess and \foo are not defined.

However, if I attempt to use this in a nested fashion

\command{foo=\command{MyDef}}

I get the error message

Missing \endcsname inserted.

<to be read again> 
              \protect 
l.25 \command{foo=\command{MyDef}}

Question:

I have a feeling that the solution is in the question listed in the references but I don't know exactly how to \protect things. So, what changes can I make to the \command macro so that I can uncomment the last line in the MWE and the proper error messages.

Reference:

Code:

\documentclass{article}
\usepackage{xcolor}

%% The last line should not produce any error 
%% messages (red text) if these two are defined.
%
%\newcommand*{\foo}{}%
%\newcommand*{\MyDef}{}%

\newcommand{\command}[1]{%
    \textbf{\textbackslash#1}%
    %% Since this is used to typeset macro names, we
    %% can check for typos by ensuring the macro exists
    \ifcsname#1\endcsname%
    \else%
        \par\fcolorbox{red}{red!20}{\textcolor{blue}{\textbackslash#1} is not defined.}
    \fi%
}%


\begin{document}
To bold text use \command{textbf} or \command{bfseriess}.

A useful token is \command{foo}.

This token needs to be set with: 
%\command{foo=\command{MyDef}}
\end{document}
share|improve this question
1  
This is not due to nested \ifcsname. For example, if you change the definition to \newcommand\command[1]{(#1 is \ifcsname#1\endcsname \else not \fi defined)} then the document compiles correctly. –  Aditya Jan 21 '13 at 1:55
    
@Aditya: Hmmm, you are indeed correct. So its just my fancy formatting that is getting the way somehow? –  Peter Grill Jan 21 '13 at 1:57
    
Yes, but I don't know the LaTeX internals well to explain why that is happening. –  Aditya Jan 21 '13 at 1:59
    
@Aditya: Yeah, thought it was a simple matter of adding a few \protects here and there, but haven't guessed the correct sequence yet. –  Peter Grill Jan 21 '13 at 2:00
1  
The problem is the \protect that's hidden in the definition of \textbf. You can trigger the error message e.g. with \DeclareRobustCommand\foo{}\csname\foo\endcsname –  cgnieder Jan 21 '13 at 10:16
show 4 more comments

3 Answers

up vote 4 down vote accepted

I'm not at all sure what the intended behaviour is when the argument to \command isn't a command name, but this does something:

enter image description here

\documentclass{article}
\usepackage{xcolor}

%% The last line should not produce any error 
%% messages (red text) if these two are defined.
%
%\newcommand*{\foo}{}%
%\newcommand*{\MyDef}{}%

\protected\def\command#1{%
    \textbf{\textbackslash#1}%
    %% Since this is used to typeset macro names, we
    %% can check for typos by ensuring the macro exists
    \ifcsname\detokenize{#1}\endcsname%
    \else%
        \par\fcolorbox{red}{red!20}{\textcolor{blue}{\textbackslash#1} is not defined.}
    \fi%
}%


\begin{document}
To bold text use \command{textbf} or \command{bfseriess}.

A useful token is \command{foo}.

This token needs to be set with: 
\command{foo=\command{MyDef}}
\end{document}

Or updated as requested in comments to strip the argument before =

enter image description here

\documentclass{article}
\usepackage{xcolor}

%% The last line should not produce any error 
%% messages (red text) if these two are defined.
%
%\newcommand*{\foo}{}%
%\newcommand*{\MyDef}{}%

\protected\def\command#1{%
    \textbf{\textbackslash#1}%
    %% Since this is used to typeset macro names, we
    %% can check for typos by ensuring the macro exists
    \ifcsname\expandafter\beforeeq\detokenize{#1}=\beforeeq\endcsname%
    \else%
        \par\fcolorbox{red}{red!20}{\textcolor{blue}{\textbackslash\expandafter\beforeeq\detokenize{#1}=\beforeeq} is not defined.}
    \fi%
}%

\def\beforeeq#1=#2\beforeeq{#1}

\begin{document}
To bold text use \command{textbf} or \command{bfseriess}.

A useful token is \command{foo}.

This token needs to be set with: 
\command{foo=\command{MyDef}}
\end{document}
share|improve this answer
    
For the nested usage, I would like to be able to use something like \StrBefore{\detokenize{#1}}{=}[\MacroName] so that I could check for \ifcsname\MacroName\endcsname but can't quite figure how to get that to work in this case. –  Peter Grill Jan 21 '13 at 12:05
    
@PeterGrill so in this example you want to test for foo? (This seems a very odd requirement:-) (Update on way....) –  David Carlisle Jan 21 '13 at 12:14
    
I agree that is strange, but I am only trying to make it work for the cases I need, not a general solution as this is for internal documentation purposes. This does indeed work as I need but was hoping that it used xstring so that I could easily expand it if needed to handle other cases. –  Peter Grill Jan 21 '13 at 21:39
add comment

I would go for something like:

\documentclass{article}
\begin{document}
\makeatletter
\def\mytypeset#1{%
\def\command##1{%
     \ifcsname##1\endcsname%
        \expandafter\@firstoftwo
    \else%
        \expandafter\@secondoftwo
    \fi%
}%
 \def\z{\detokenize{#1}}
 \command{\z}{\fbox{TRUE}}{\fbox{FALSE}}
}


\mytypeset{deff{f}f}

\mytypeset{def}

\mytypeset{foo=\command{MyDef}}
\end{document}

Note that when you are checking for a "nested" command, as a matter of fact the code checks to see if:

  foo=\command{MyDef}

is a defined macro! With csname...endcsname, arbitrary commands of any characters ca be used. You can have a command !$%.#!.o>,p\def} for example. You can add the follow snippet to the example that demonstrates the concept.

\expandafter\def\csname My$Def\endcsname{MyDef...}

\mytypeset{My$Def}

% prints MyDef...
\csname My$Def\endcsname

\My$Def now is a command and prints MyDef.., when executed.

share|improve this answer
    
Yeah, am trying to figure out how to handle the nested case since what I initially put together would fail miserably for that case. –  Peter Grill Jan 21 '13 at 9:11
    
@PeterGrill ...tricky stuff! : –  Yiannis Lazarides Jan 21 '13 at 9:16
add comment

By simply imitating the definition of \doifundefined from ConTeXt (defined in file syst-gen.mkii), adding \detokenize to the definition works.

\newcommand\command[1]
    {\textbf{\textbackslash#1}%
    %% Since this is used to typeset macro names, we
    %% can check for typos by ensuring the macro exists
     \ifcsname\detokenize{#1}\endcsname%
    \else%
        \par\fcolorbox{red}{red!20}{\textcolor{blue}{\textbackslash#1} is not defined.}
    \fi%
}% 

enter image description here

share|improve this answer
    
Interesting... Indeed it does appear to fix the situation, in that the code compiles. However, the macro is not yielding correct results. If you un-comment the \newcommand*{\foo}{} and \newcommand*{\MyDef}{}, the only error message should be the one for bfseriess, but this is not the case. .... Oh, wait, I think that is what @percusse's comment is about... –  Peter Grill Jan 21 '13 at 3:41
    
When you uncomment the \newcommands, the macro is checking if \csname foo=\textbf{\textbackslash MyDef}\endcsname is defined or not. Clearly, that montrosity is not defined. –  Aditya Jan 21 '13 at 6:30
    
Yeah that is what I meant about precusse's comment. I need to rethink how to handle the nested case properly. –  Peter Grill Jan 21 '13 at 7:30
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.