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.

Khaurum
  • 245

2 Answers2

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. :)

Paulo Cereda
  • 44,220
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 }
 }
egreg
  • 1,121,712