5

I am experimenting with LaTeX3 features and get a strange behaviour in a combination from index and l3regex. I try to replace all occurrences of :<space> with the exclamation mark sign, so that an entry in the list gets a subentry in the Index.

\documentclass[]{scrbook}
\usepackage{index}
\usepackage{xparse}
\usepackage{l3regex}

\makeindex

\ExplSyntaxOn
\tl_new:N \l_demo_tl
\cs_new:Npn \demo #1 {
    \tl_set:Nn \l_demo_tl {#1}
    \regex_replace_all:nnN { :\h } {!} \l_demo_tl
    \tl_use:N \l_demo_tl
}
\ExplSyntaxOff

\NewDocumentCommand{\prozessIndex}{m}{\index{\demo{#1}}}

\NewDocumentCommand{\prozessIndexList}{>{\SplitList{,}}m}{%
    \ProcessList{#1}{\prozessIndex}%
}

\begin{document}
Here some Text
\prozessIndexList{Hello,Hello: World, Muh,Muh!Kuh}    
\printindex
\end{document}

But the Result ist

Hello!World ,1
Hello , 1
Muh
    Kuh , 1
Muh , 1

But it should be

Hello , 1
    World , 1
Muh
    Kuh , 1
Muh , 1

Is there a other way to go?

  • 1
    Thank you. I'am here very often. But this is my first question :) – Landarzar Mar 9 '15 at 13:49
6

Look in the .idx file and you see

\indexentry {\demo {Hello}}{1}
\indexentry {\demo {Hello: World}}{1}
\indexentry {\demo {Muh}}{1}
\indexentry {\demo {Muh!Kuh}}{1}

You don't want makeindex to see \demo but rather the result of running demo so:

\documentclass[]{scrbook}
\usepackage{index}
\usepackage{xparse}
\usepackage{l3regex}

\makeindex

\ExplSyntaxOn
\tl_new:N \l_demo_tl
\cs_new:Npn \demo #1 {
    \tl_set:Nn \l_demo_tl {#1}
    \regex_replace_all:nnN { :\h } {!} \l_demo_tl
    \tl_use:N \l_demo_tl
}
\ExplSyntaxOff

\NewDocumentCommand{\prozessIndex}{m}{\demo{\index{#1}}}

\NewDocumentCommand{\prozessIndexList}{>{\SplitList{,}}m}{%
    \ProcessList{#1}{\prozessIndex}%
}

\begin{document}
Here some Text
\prozessIndexList{Hello,Hello: World, Muh,Muh!Kuh}    
\printindex
\end{document}

Now the idx looks like

\indexentry {Hello}{1}
\indexentry {Hello!World}{1}
\indexentry {Muh}{1}
\indexentry {Muh!Kuh}{1}
  • It work like a charm. – Landarzar Mar 9 '15 at 13:41
4

If you replace index with makeidx, you get several errors about \h being an undefined control sequence, which is a hint that something is going wrong; but the main point is that the .idx file ends up to contain

\indexentry{\tl_set:Nn {Hello}\regex_replace_all:nnN {:}{!}}{1}
\indexentry{\tl_set:Nn {Hello: World}\regex_replace_all:nnN {:}{!}}{1}
\indexentry{\tl_set:Nn {Muh}\regex_replace_all:nnN {:}{!}}{1}
\indexentry{\tl_set:Nn {Muh!Kuh}\regex_replace_all:nnN {:}{!}}{1}

which is hardly what you'd like to have. With the \index package you'd get

\indexentry{\demo {Hello}

and so on, so the problem is the same.

You have to do the replacement before feeding the word to \index. and the normal xparse facilities are not sufficient.

\documentclass[]{scrbook}
\usepackage{makeidx}
\usepackage{xparse}
\usepackage{l3regex}

\makeindex

\ExplSyntaxOn

\NewDocumentCommand{\indexlist}{m}
 {% pass control to an internal function
  \landarzar_indexlist:n { #1 }
 }

% two variables
\tl_new:N \l_landarzar_temp_tl
\seq_new:N \l_landarzar_list_seq

% translate `\index` into an internal function
\cs_set_eq:NN \landarzar_index:n \index
% define a variant that uses a value, instead of an explicit argument
\cs_generate_variant:Nn \landarzar_index:n { V }

\cs_new_protected:Npn \landarzar_indexlist:n #1
 {
  % split the input into pieces
  \seq_set_split:Nnn \l_landarzar_list_seq { , } { #1 }
  % process each item in turn
  \seq_map_inline:Nn \l_landarzar_list_seq
   {
    % pass the item through the regex replacement
    \tl_set:Nn \l_landarzar_temp_tl { ##1 }
    \regex_replace_all:nnN { :\s } { ! } \l_landarzar_temp_tl
    % emit the \index command
    \landarzar_index:V \l_landarzar_temp_tl
   }
 }
\ExplSyntaxOff

\begin{document}
Here some Text
\indexlist{Hello,Hello: World, Muh,Muh!Kuh}    
\printindex
\end{document}

Now the .idx file reads

\indexentry{Hello}{1}
\indexentry{Hello!World}{1}
\indexentry{Muh}{1}
\indexentry{Muh!Kuh}{1}

Explanation

The argument is split into components using a sequence (which is basically what \ProcessList does). Then each item is passed through \regex_replace_all:nnN and the correct \index command is called, using the value of the token list.

Note

With makeidx or imakeidx the \index macro does an expansion step of its input, which doesn't happen with the index package.


A variant code that allows for multiple indices (I use imakeidx, but it can be done similarly with index). The code is pretty similar, the only relevant difference is how to cope with the optional argument.

\documentclass[]{scrbook}
\usepackage{xparse}
\usepackage{l3regex}
\usepackage{imakeidx}

\makeindex
\makeindex[name=landarzarx]

\ExplSyntaxOn

\NewDocumentCommand{\indexlist}{O{}m}
 {
  \landarzar_indexlist:nn { #1 } { #2 }
 }

\tl_new:N \l_landarzar_temp_tl
\seq_new:N \l_landarzar_list_seq

\cs_new_protected:Npn \landarzar_index:n #1
 {
  \index{#1}
 }
\cs_new_protected:Npn \landarzar_index:nn #1 #2
 {
  \index[#1]{#2}
 }
\cs_generate_variant:Nn \landarzar_index:n { V }
\cs_generate_variant:Nn \landarzar_index:nn { nV }

\cs_new_protected:Npn \landarzar_indexlist:nn #1 #2
 {
  \seq_set_split:Nnn \l_landarzar_list_seq { , } { #2 }
  \seq_map_inline:Nn \l_landarzar_list_seq
   {
    \tl_set:Nn \l_landarzar_temp_tl { ##1 }
    \regex_replace_all:nnN { :\s } { ! } \l_landarzar_temp_tl
    \tl_if_empty:nTF { #1 }
     { \landarzar_index:V \l_landarzar_temp_tl } % no optional argument
     { \landarzar_index:nV { #1 } \l_landarzar_temp_tl } % optional argument
   }
 }
\ExplSyntaxOff

\begin{document}
Here some Text

\indexlist{Hello,Hello: World, Muh,Muh!Kuh}
\indexlist[landarzarx]{Hello,Hello: World, Muh,Muh!Kuh}

\printindex
\printindex[landarzarx]

\end{document}

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.