18

Is there any way to mark words in a text for the use of them in a list later on?

Example:

Hi my name is \addmark{Kris}, I need a \addmark{list} of words containing all the 
\addmark{marked words} by the end of the \addmark{document}.
\begin{itemize}
\item \addmark{fancy list}
\end{itemize}

\printmark

to look like this:

Hi my name is Kris, I need a list of words containing all the marked words by the end of the document.

  • fancy list

Kris, list, marked words, document and fancy list.

  • 3
    Welcome to TeX.SX! Do you mean something like an index? The glossaries package can do something like that for you. – mafp Nov 3 '13 at 8:39
  • But as far as I understood the glossaries package only allows predefined terms right? I would like to be able to define the terms for indexing in the text. – Khaurum Nov 3 '13 at 8:45
  • 3
    You can use makeidx. – Marco Daniel Nov 3 '13 at 8:48
  • 1
    makeidxseems good, but the \makeindex gives a list with index as title and I only need the indexed words in a paragraph as shown in my example - is there any workarround? – Khaurum Nov 3 '13 at 9:07
22

How about something with expl3 and sequences? :)

\documentclass{article}

\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \g_khaurum_wordlist_seq

\NewDocumentCommand{ \addmark }{ m }{
   \seq_gput_right:Nn \g_khaurum_wordlist_seq { #1 }
   #1
}

\NewDocumentCommand{ \printmarks }{s}{
    \seq_gremove_duplicates:N \g_khaurum_wordlist_seq
    \seq_use:Nnnn \g_khaurum_wordlist_seq { ~ and ~ } { , ~ } { ~ and ~ }
    \IfBooleanT { #1 } {
        \seq_gclear:N \g_khaurum_wordlist_seq
    }
}

\ExplSyntaxOff

\begin{document}

Hi my name is \addmark{Kris}, I need a \addmark{list} of words
containing all the \addmark{marked words} by the end of the
\addmark{document}.

\begin{itemize}
\item I'm a list of \addmark{items}
\item Ducks are \addmark{cool}
\end{itemize}

\vspace{1em}

\printmarks

\vspace{1em}

Another \addmark{value} here.

\vspace{1em}

\printmarks*

\vspace{1em}

The starred version cleared the \addmark{sequence}, so now
we can add  new \addmark{entries} again from start.

\vspace{1em}

\printmarks

\end{document}

Quack

When using \printmarks*, the sequence is printed and then cleared; so we can start a fresh sequence and add new entries afterwards.

Hope it helps. :)

Thanks to Marco Daniel and egreg for providing improvements to the code. :)

  • Exactly what I needed! – Khaurum Nov 3 '13 at 9:45
  • I just figured out that I can not add marks in a list - why is that? – Khaurum Nov 3 '13 at 10:21
  • 1
    Maybe you should use a global sequence. So you can use inside a table too. – Marco Daniel Nov 3 '13 at 10:39
  • 1
    @Marco: ah. I replaced \seq_put_right:Nn by \seq_gput_right:Nn. Do you believe is that enough? – Paulo Cereda Nov 3 '13 at 11:31
  • 1
    @Khaurum: I changed the code slightly, so now it might work. I'll just wait for the L3 wizards to check if it's correct. :) – Paulo Cereda Nov 3 '13 at 11:32
7

Since Paulo has already presented a LaTeX3 version, I'll stick to a classical approach. I only add a switch for printing red the marked words in the text, that may reveal useful during document writing.

\documentclass{article}
\usepackage{xcolor}

\newif\ifshowmarked
\makeatletter
\newtoks\mark@toks
\mark@toks={\@gobble}
\newcommand{\mark@comma}{, }
\newcommand{\addmark}[1]{%
  \@ifundefined{addmark@\detokenize{#1}}
   {% New marked word
    \global\@namedef{addmark@\detokenize{#1}}{#1}%
    \global\mark@toks=\expandafter{\the\mark@toks\mark@comma\@nameuse{addmark@\detokenize{#1}}}%
   }
   {
    % Already marked word, do nothing
   }%
   \ifshowmarked
     \textcolor{red}{#1}%
   \else
     #1%
   \fi
}

\newcommand{\printmarks}{%
  \the\mark@toks\relax
  \@ifstar{\global\mark@toks{\@gobble}}{}%
}
\makeatletter

\begin{document}

Hi my name is \addmark{Kris}, I need a \addmark{list} of words
containing all the \addmark{marked words} by the end of the
\addmark{document}.

\begin{itemize}
\item I'm a list of \addmark{items}
\item Ducks are \addmark{cool}
\end{itemize}

\vspace{1em}

\printmarks

\vspace{1em}

Another \addmark{value} here.

\vspace{1em}

\printmarks*

\vspace{1em}

\showmarkedtrue

The starred version cleared the \addmark{sequence}, so now
we can add  new \addmark{entries} again from start.

\vspace{1em}

\printmarks

\end{document}

The argument to \addmark is used to build a control sequence; if this command is already defined, we do nothing, otherwise define the control sequence and add it to a token register preceded by a comma and a space. The token register is initialized to contain \@gobble, so the first comma will not be printed. Finally, the word is printed (in red if the switch is on).

The \printmarked macro works in the same way: the token register's content is delivered; if the *-variant is used, the token register is reinitialized.

enter image description here

Note that the LaTeX3 is superior, because it allows for using the list of marked words in many different ways. It's doable also in the “classical” version, but reinventing the wheel many times. Also, no \detokenize trick is necessary with LaTeX3 to cope with special characters in the marked words, since the word is simply stored as is.

Just comparing the code clearly shows the superiority of the LaTeX3 approach.


Adding the switch to the LaTeX3 version is easy. Add

\bool_new:N \g_khaurum_highlight_bool
\NewDocumentCommand{\showmarkedtrue}{}
 {
  \bool_gset_true:N \g_khaurum_highlight_bool
 }
\NewDocumentCommand{\showmarkedfalse}{}
 {
  \bool_gset_false:N \g_khaurum_highlight_bool
 }

and change the definition of \addmark to

\NewDocumentCommand{ \addmark }{ m }
 {
  \seq_gput_right:Nn \g_khaurum_wordlist_seq { #1 }
  \bool_if:NTF \g_khaurum_highlight_bool { \textcolor}{red}{#1} } { #1 }
 }

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.