# Differentiate reference based on type (problem with argument expansion)

Since I use unnumbered sections, I'd like to have a variation of \autoref that uses names instead of numbers in the case of sectioning, but keeps its usual behaviour for other environments. I've come up with this naive piece of code, but I don't get it to work.

\documentclass{article}
\usepackage{xstring, hyperref, nameref}
\setcounter{secnumdepth}{-1}
\makeatletter
\newcommand{\KSautoref}[1]{
\edef\KS@RefType{\expandafter\@fourthoffive\csname r@#1\endcsname}
\newif\ifKSSectioning\KSSectioningfalse
\IfBeginWith{\KS@RefType}{chapter}{\KSSectioningtrue}{}
\IfBeginWith{\KS@RefType}{section}{\KSSectioningtrue}{}
\IfBeginWith{\KS@RefType}{subsection}{\KSSectioningtrue}{}
\IfBeginWith{\KS@RefType}{subsubsection}{\KSSectioningtrue}{}
\IfBeginWith{\KS@RefType}{paragraph}{\KSSectioningtrue}{}
\IfBeginWith{\KS@RefType}{subparagraph}{\KSSectioningtrue}{}
\ifKSSectioning\chaptername\nameref{#1}\else\autoref{#1}\fi
}
\makeatother
\begin{document}
\section{Test} \label{test}
See \KSautoref{test}.
\end{document}


When I try to compile this, I get the following error:

! Argument of \@fourthoffive has an extra }.
<inserted text>
\par
l.19 See \KSautoref{test}
.
?


Using \tracingall I get this additional information:

{\edef}
{\expandafter}
{\csname}

\@fourthoffive #1#2#3#4#5->#4
#1<-\r@test


How can I get \r@test to expand as well, so that it's not used as just the first argument?

If there is a package that provides the functionality, I'd rather use that, but still I'd like to know the bit about expansion...

• do you really want \chapername? and besides with numbering off, starting with sections, hyperref will (I think) always use section*.<number> so \nameref says section for things which are not necessarily sections. It is pretty hopeless if things are not numbered to use \label/\ref (or variants). – user4686 Nov 6 '13 at 15:21
• I used \chaptername as a placeholder for a custom command, but since I didn't get this far with the above code, I never saw the error. Apart from that, I do intend to use this custom command, as \nameref just prints the name without the type as \autoref does. – pazathoth Nov 12 '13 at 14:54

You're doing only one expansion, but you need two. With

\expandafter\@fourthoffive\csname r@#1\endcsname


and #1=test as in the example, you get

\expandafter\@fourthoffive\csname r@test\endcsname


which becomes

\@fourthoffive\r@test


So you need \expandafter\expandafter\expandafter.

Here's a better version:

\documentclass{book}
\usepackage{xstring, hyperref, nameref}
\setcounter{secnumdepth}{-1}
\makeatletter
\newif\ifKSSectioning
\newcommand{\KSautoref}[1]{%
\KSSectioningfalse
\expandafter\ifx\csname r@#1\endcsname\relax
\ref{#1}%
\else
\protected@edef\KS@RefType{\expandafter\expandafter\expandafter\@fourthoffive\csname r@#1\endcsname}%
\IfBeginWith{\KS@RefType}{chapter}{\KSSectioningtrue}{}%
\IfBeginWith{\KS@RefType}{section}{\KSSectioningtrue}{}%
\IfBeginWith{\KS@RefType}{subsection}{\KSSectioningtrue}{}%
\IfBeginWith{\KS@RefType}{subsubsection}{\KSSectioningtrue}{}%
\IfBeginWith{\KS@RefType}{paragraph}{\KSSectioningtrue}{}%
\IfBeginWith{\KS@RefType}{subparagraph}{\KSSectioningtrue}{}%
\ifKSSectioning\chaptername~\nameref{#1}\else\autoref{#1}\fi
\fi
}
\makeatother
\begin{document}
\section{Test} \label{test}
See \KSautoref{test}.
\end{document}


The added conditional is to cope with the case the reference is not already set; I moved the \newif outside the definition, as it's useless constantly creating a new conditional, all it suffices is to set it to false at the beginning of the macro.

I also used \protected@edef in case the title contains macros such as \emph that don't survive \edef.

Here's an implementation using expl3 and regular expressions.

\documentclass{book}
\usepackage{xparse,l3regex, hyperref, nameref}
\setcounter{secnumdepth}{-1}

\ExplSyntaxOn
% Document level commands
\NewDocumentCommand \KSautoref { m }
{
\pazathoth_ks_autoref:n { #1 }
}

% variables
\tl_new:N \l__pazathoth_type_tl
\regex_const:Nn \c_pazathoth_class_regex
{
\A (chapter|section|subsection|subsubsection|paragraph|subparagraph) .*
}

% internal functions
\cs_new_protected:Npn \pazathoth_ks_autoref:n #1
{
\cs_if_eq:cNTF { r@#1 } \scan_stop:
{ % if the reference is undefined go to the standard method
\ref { #1 }
}
{ % else scan for a named reference
\__pazathoth_ks_autoref:cn { r@#1 } { #1 }
}
}

\cs_new_protected:Npn \__pazathoth_ks_autoref:Nn #1 #2
{
% get the fourth item in r@#1
\tl_set:Nx \l__pazathoth_type_tl { \tl_item:Vn #1 { 4 } }
% check if the fourth item starts with a name
\regex_replace_all:NnNTF \c_pazathoth_class_regex { \1 } \l__pazathoth_type_tl
{ % the replacement was successful; use the section name
\use:c { \l__pazathoth_type_tl name } \nobreakspace \nameref { #2 }
}
{ % no name, use \autoref
\autoref { #2 }
}
}

% function variants
\cs_generate_variant:Nn \cs_if_eq:NNTF { c }
\cs_generate_variant:Nn \tl_item:nn { V }
\cs_generate_variant:Nn \__pazathoth_ks_autoref:Nn { c }

\ExplSyntaxOff

\providecommand{\sectionname}{Section}

\begin{document}
\section{Test \emph{x}} \label{test}
See \KSautoref{test}.

\begin{enumerate}
\item\label{item} x
\end{enumerate}

See \KSautoref{item}.

\end{document}


• see my comment to OP. – user4686 Nov 6 '13 at 15:22
• @jfbu It's true that only chapter and section are distinguished, but it is possible (not easy, I guess) to differentiate. – egreg Nov 6 '13 at 15:25
• Thanks for the suggestions, I missed a few things there... The crucial point was that on the first run, the reference is not yet set, so \@fourthoffive would always fail. Triple \expandafter didn't help either, so I discarded it and thought it was a problem with expansion... – pazathoth Nov 12 '13 at 13:23