From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001
From: Norbert Preining <norbert@preining.info>
Date: Mon, 2 Sep 2019 13:46:59 +0900
Subject: Initial commit

---
 macros/latex/contrib/xpeek/xpeek.dtx | 700 +++++++++++++++++++++++++++++++++++
 1 file changed, 700 insertions(+)
 create mode 100644 macros/latex/contrib/xpeek/xpeek.dtx

(limited to 'macros/latex/contrib/xpeek/xpeek.dtx')

diff --git a/macros/latex/contrib/xpeek/xpeek.dtx b/macros/latex/contrib/xpeek/xpeek.dtx
new file mode 100644
index 0000000000..007ca4e6e7
--- /dev/null
+++ b/macros/latex/contrib/xpeek/xpeek.dtx
@@ -0,0 +1,700 @@
+% \iffalse meta-comment
+% !TeX program = pdflatex
+% !TeX encoding = utf-8
+% !TeX spellcheck = en_US
+%<*internal>
+\begingroup
+%</internal>
+%<*install>
+\input{l3docstrip.tex}
+\keepsilent
+\askforoverwritefalse
+\preamble
+----------------------------------------------------------------
+
+xpeek: Define commands that peek ahead in the input stream
+
+Copyright © 2012 by Joel C. Salomon.
+
+This work consists of the file  xpeek.dtx,
+          and the derived files xpeek.ins,
+                                xpeek.pdf, &
+                                xpeek.sty.
+It may be distributed and/or modified under the conditions of the
+LaTeX Project Public License (LPPL), either version 1.3c of this
+license or (at your option) any later version.  The latest version
+of this license is at ‹http://www.latex-project.org/lppl.txt›.
+
+This work is “maintained” (per LPPL maintenance status) by
+Joel C. Salomon ‹joelcsalomon@gmail.com›.
+
+----------------------------------------------------------------
+\endpreamble
+\declarepreamble\minimalpreamble
+\endpreamble
+\postamble
+\endpostamble
+\usedir{tex/latex/xpeek}
+\generate{
+  \file{\jobname.sty}{\from{\jobname.dtx}{package}}
+}
+\nopreamble\nopostamble
+\generate{
+  \file{\jobname.tests}{\from{\jobname.dtx}{tests}}
+}
+\generate{
+  \file{\jobname-test.tex}{\from{\jobname.dtx}{testsuite,tests}}
+}
+%</install>
+%<install>\endbatchfile
+%<*internal>
+\usepreamble\minimalpreamble
+\usepostamble\defaultpostamble
+\usedir{source/latex/xpeek}
+\generate{
+  \file{\jobname.ins}{\from{\jobname.dtx}{install}}
+}
+\endgroup
+%</internal>
+%<*driver>
+\pdfobjcompresslevel=0
+\RequirePackage[utf8]{inputenc}
+\documentclass[full]{l3doc}
+\usepackage{xpeek}
+\usepackage{xspace}
+\usepackage[all]{foreign}
+\usepackage{qstest}
+
+^^A Set up formatting options
+
+^^A Format URLs surrounded by guillemets
+\DeclareUrlCommand\url
+  {\def\UrlLeft##1\UrlRight{\text{‹}\href{##1}{##1}\text{›}}}
+\DeclareUrlCommand\email
+  {\def\UrlLeft##1\UrlRight{\text{‹}\href{mailto:##1}{##1}\text{›}}}
+\urlstyle{same}
+
+^^A Make Unicode curly quotes (“”) behave correctly
+\AtBeginDocument{
+  \sfcode\csname\encodingdefault\string\textquotedblright\endcsname=0
+}
+\xspaceaddexceptions{”}
+
+^^A Define indented verbatim environment
+\DefineVerbatimEnvironment{IVerbatim}{Verbatim}%
+  {xleftmargin=2.5\rmemwidth, gobble=4}
+\newlength\rmemwidth
+\AtBeginDocument{
+  \setlength\rmemwidth{\fontdimen6\font}
+}
+
+^^A Enable line continuations in `syntax` environments
+\NewDocumentCommand \fixsyntaxlines {}
+  {\def\^^M{\unskip\space\ignorespaces}}
+
+\begin{document}
+  \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \changes{Version 0.1}{2012/08/09}{First useful version}
+%
+% \GetFileInfo{\jobname.sty}
+%
+% \DoNotIndex
+%   {
+%     \ProvidesExplPackage, \RequirePackage, \NewDocumentCommand,
+%     \ExplSyntaxOn, \ExplSyntaxOff
+%     \documentclass, \usepackage,
+%     \begin, \end,
+%     \xspace,
+%     \bool_new:N, \bool_set_true:N, \bool_set_false:N, \bool_if:NTF,
+%     \hbox_set:Nn, \box_wd:N, \l_tmpa_box, \l_tmpb_box,
+%     \cs_new_protected:Npn,
+%     \dim_use:N,
+%     \peek_after:Nw,
+%     \prg_new_protected_conditional:Npnn,
+%     \prg_return_true:, \prg_return_false:,
+%     \tl_new:N, \tl_clear:N, \tl_const:Nn, \tl_set:Nn,
+%     \tl_put_right:Nn,
+%     \tl_map_inline:Nn, \tl_map_break:,
+%     \c_empty_tl,
+%     \token_if_eq_charcode:NNT,
+%     \xsp, \c_xsp_exceptions_tl,
+%   }
+%
+% \NewDocumentCommand \releasenote {}
+%   {This file describes release \fileversion, last revised \filedate.}
+% \title{The \pkg{\jobname} package\thanks\releasenote}
+%
+% \urldef{\urlxthinsp}{\url}{http://tex.stackexchange.com/a/59542/2966}
+% \urldef{\urlxflw}{\url}{http://tex.stackexchange.com/q/63568/2966}
+% \urldef{\urlxign}{\url}{http://tex.stackexchange.com/q/63971/2966}
+% \urldef{\urlltxl}
+%   {\url}{http://thread.gmane.org/gmane.comp.tex.latex.latex3/2894}
+% \NewDocumentCommand \acknote {}
+%   {The original code is due to Enrico “egreg” Gregorio,
+%    based on an answer he gave to a question of mine on
+%    \href{http://tex.stackexchange.com}{\TeX.SX};
+%    see \urlxthinsp.
+%    Enrico, Joseph Wright, Bruno Le Floch, \& Clemens Niederberger
+%    helped iron out the implementation;
+%    Bruno also wrote the initial version of the documentation
+%    and the near-final version of much of the code.
+%    See \urlxflw, \urlxign, \& \urlltxl.}
+% \author{Joel C. Salomon\\\email{joelcsalomon@gmail.com}\thanks\acknote}
+% \date{Released \filedate}
+%
+% \maketitle
+%
+% \begin{abstract}
+%   The \pkg{\jobname} package provides functions
+%   for defining \& managing commands which,
+%   like the familiar \cs{xspace},
+%   “peek” ahead at what follows them in the input stream.
+%   They compare this token against a stored list
+%   and choose an action depending on whether there was a match.
+% \end{abstract}
+%
+% \tableofcontents
+%
+% \begin{documentation}
+%
+% \section{Introduction}
+%
+% This package provides functions for defining \cs{xspace}-like commands.
+% Such commands “peek” ahead into the input stream
+% to see what follows their invocations;
+% their behavior varies depending on what it is that comes next.
+%
+% Probably the most common \LaTeX\ command of this sort is \cs{textit}.
+% It looks ahead at the token following its argument
+% and decides whether to insert italic correction afterward.
+% Table~\ref{tab:itcor} compares the use of \cs{textit}
+% against using the font-switch command \cs{itshape}
+% with \& without explicit italic correction.
+% \begin{table}[hbp]\centering
+%   \begin{tabular}{|lll|}
+%   \hline
+%     |{\itshape half}hearted| &
+%      {\itshape half}hearted  &
+%         (overlap)\\
+%     |{\itshape half\/}hearted| &
+%      {\itshape half\/}hearted  &
+%         (correct)\\
+%     |\textit{half}hearted| &
+%       \textit{half}hearted &
+%         (correctly including italic correction)\\
+%     ^^A There’s a bug in the package `trace` which breaks suppression
+%     ^^A of italic correction; see latex3/svn-mirror#96 for details.
+%     ^^A As a temporary work-around, include `\nocorr` where needed.
+%     |by {\itshape half}.| &
+%      by {\itshape half}.  &
+%         (correct)\\
+%     |by {\itshape half\/}.| &
+%      by {\itshape half\/}.  &
+%         (too wide)\\
+%     |by \textit{half}.| &
+%      by \textit{half\nocorr}.  &
+%         (correctly omitting italic correction)\\
+%   \hline
+%   \end{tabular}
+%   \caption{Automatic italic correction with \cs{textit}}
+%   \label{tab:itcor}
+% \end{table}
+% Notice how \cs{textit} seems to just “do the right thing”
+% when it comes to inserting or omitting italic correction.
+%
+% It is, unfortunately, not hard to confuse \cs{textit}.
+% As an example, consider the following code:
+% \begin{IVerbatim}
+%   \NewDocumentCommand \naif {} {\textit{na\"\i f}}
+%   He was a true \naif.
+% \end{IVerbatim}
+% This yields
+% \begin{quote}
+%   \NewDocumentCommand \naif {} {\textit{na\"\i f\nocorr}}
+%   He was a true \naif.
+% \end{quote}
+% So far, so good.
+% But using this macro in middle of a sentence
+% may produce results the naïve user does not expect.
+% For example, try
+% \begin{IVerbatim}
+%   He was such a \naif he expected this example to work.
+% \end{IVerbatim}
+% yielding
+% \begin{quote}
+%   \NewDocumentCommand \naif {} {\textit{na\"\i f}}
+%   He was such a \naif he expected this example to work.
+% \end{quote}
+% Oops.
+% We forgot that command words eat the spaces that follow them.
+%
+% This is usually the point where \TeX perts explain
+% about following command words with explicit spaces,
+% \eg, writing “|such a \naif\ he |…”,
+% or following such commands with an empty group,
+% “|such a \naif{} he |…”, \eg.
+%
+% Others, more pragmatically-inclined,
+% will mention the \pkg{xspace} package.
+% This is the sort of use-case it was developed for, after all,
+% so let’s try it:
+% \begin{IVerbatim}
+%   \NewDocumentCommand \naif {} {\textit{na\"\i f}\xspace}
+%   He was such a \naif he expected this example to work.
+% \end{IVerbatim}
+% This produces
+% \begin{quote}
+%   \NewDocumentCommand \naif {} {\textit{na\"\i f}\xspace}
+%   He was such a \naif he expected this example to work.
+% \end{quote}
+% Even “such a \foreign{naïf}” can get the right result sometimes.
+%
+% But you see, there’s a kinda hitch.^^A
+% \footnote{Cue Malcolm Reynolds: “Don’t complicate things.”}
+% Recall the first time we used the macro:
+% \begin{IVerbatim}
+%   He was a true \naif.
+% \end{IVerbatim}
+% Now it yields this:
+% \begin{quote}
+%   \NewDocumentCommand \naif {} {\textit{na\"\i f}\xspace}
+%   He was a true \naif.
+% \end{quote}
+% Notice that ugly space between the \textit{f} and the period?
+% That’s italic correction being added where it doesn’t belong.
+%
+% The trouble lies in how \LaTeX\ implements the \cs{textit} logic.
+% The macro is clever enough to look ahead,
+% seeking for short punctuation (\eg, the comma \& period)
+% which should not be preceded by italic correction.
+% What the macro finds first, though,
+% is that invocation of \cs{xspace} we added.
+% That’s not one of the “no italic correction” tokens,
+% so the correction gets added and the sentence looks ugly.
+%
+% That’s where this package comes in.
+%
+% (As a side note, Phillip G. Ratcliffe’s \pkg{foreign} package,
+%  bundled with both MiK\TeX\ and \TeX~Live
+%  and available from CTAN at \url{http://ctan.org/pkg/foreign},
+%  is an excellent tool for this sort of foreign-word macro
+%  and was one of the inspirations for this package.)
+%
+% Commands generated via the tools the \pkg{xpeek} package provides
+% can peek ahead in the input stream,
+% like \cs{textit} and \cs{xspace},
+% checking whether the next token is on their match-list.
+% What \pkg{xpeek} adds is
+% the ability to ignore certain tokens during that peek-ahead.
+% These ignored tokens are not lost;
+% the scan-ahead routine saves them
+% and they are restored when the command executes.
+%
+% \section{Documentation}
+%
+% At this point in the development of \pkg{xpeek},
+% only one set of follower-dependent functions is defined.
+% Later versions will provide tools for defining such function sets.
+%
+% \subsection{Match- \& Ignore-Lists}
+%
+% \begin{variable}{\g_xpeek_matchlist_tl, \g_xpeek_ignorelist_tl}
+%   \begin{syntax}
+%     \cs{tl_gput_right:Nn} \cs{g_xpeek_\meta{list}_tl} \Arg{tokens}
+%     \cs{tl_gremove_all:Nn} \cs{g_xpeek_\meta{list}_tl} \Arg{tokens}
+%   \end{syntax}
+%   These two token-lists are the heart of \pkg{xpeek}.
+%
+%   The \textit{match-list} is similar to \pkg{xspace}’s exceptions list
+%   or \LaTeX’s \cs{nocorrlist} list for suppressing italic correction.
+%
+%   The functionality enabled by the \emph{ignore-list},
+%   on the other hand,
+%   is the \foreign{raison d’être} of this package.
+%   For example,
+%   if \LaTeX’s \cs{textit} had an ignore-list
+%   and the token \cs{xspace} were in that list,
+%   then the definition
+%   \begin{IVerbatim}[gobble=6]
+%     \NewDocumentCommand \naif {} {\textit{na\"\i f}\xspace}
+%   \end{IVerbatim}
+%   from the example above
+%   would have worked properly:
+%   \cs{textit} would have ignored the \cs{xspace},
+%   looked past it \& seen the period,
+%   and therefore omitted italic correction.
+%   When that was done, \cs{xspace} would execute;
+%   it too would see the period,
+%   and therefore would not insert a space.
+% \end{variable}
+%
+% \subsection{Peek-ahead}
+%
+% \begin{function}{\xpeek_after:nw}
+%   \begin{syntax}
+%     \cs{xpeek_after:nw} \Arg{code} \meta{token}
+%   \end{syntax}
+%   Similar to \pkg{expl3}’s \cs{peek_after:Nw},
+%   this sets the test variable \cs{l_peek_token} to \meta{token}
+%   then executes the \Arg{code}.
+%   The \meta{token} remains in the input stream.
+% \end{function}
+%
+% \subsection{Collecting Ignored Tokens}
+%
+% \begin{function}{\xpeek_collect_do:nn}
+%   \begin{syntax}\fixsyntaxlines
+%     \cs{xpeek_collect_do:nn} \Arg{ignore-list} \Arg{operation} \
+%       \meta{the rest of the input-stream}
+%   \end{syntax}
+%   This collects all leading tokens in the input stream
+%   which are in the ignore-list,
+%   and passes them as an argument to the operation.
+%   For example, the invocation
+%   \begin{IVerbatim}[gobble=6]
+%     \xpeek_collect_do:nn { abc } { \foo \bar } caada
+%   \end{IVerbatim}
+%   will collect the leading “\texttt{caa}” from the input stream
+%   and pass those tokens as an argument to
+%   the operation “|\foo \bar|”.
+%   \Ie, the code above is equivalent to this:
+%   \begin{IVerbatim}[gobble=6]
+%     \foo \bar { caa } da
+%   \end{IVerbatim}
+%   (Note that it’s \cs{bar} \emph{within} the \Arg{operation}
+%    that gets the collected tokens as an argument.)
+% \end{function}
+%
+% \subsection{Searching Through Token-Lists}
+%
+% \begin{function}{\xpeek_if_in:NNTF}
+%   \begin{syntax}\fixsyntaxlines
+%     \cs{xpeek_if_in:NNTF} \cs{\meta{haystack}} \cs{\meta{needle}} \
+%       \Arg{true-code} \Arg{false-code}
+%   \end{syntax}
+%   Among the variants of \cs{tl_if_in:Nn(TF)} defined in \pkg{expl3},
+%   the form \cs{tl_if_in:NNTF} is missing.
+%   But that’s the form needed in this package,
+%   in constructions like this:
+%   \begin{IVerbatim}[gobble=6, commandchars=+\[\]]
+%     \xpeek_if_in:NNTF \g_xpeek_matchlist_tl \l_peek_token
+%       +marg[true-code] +marg[false-code]
+%   \end{IVerbatim}
+%   So we define it ourselves.
+% \end{function}
+%
+% \section{Example}
+%
+% As a demonstration,
+% here’s an example of using the \pkg{xpeek} facilities
+% to implement correct automatic italic correction \ala \cs{textit}.
+%
+% We set the match-list equal to \LaTeX’s default \cs{nocorrlist};
+% as mentioned above, \cs{xspace} needs to be in the ignore-list:
+% \begin{IVerbatim}
+%   \tl_gset:Nn \g_xpeek_matchlist_tl  { , . }
+%   \tl_gset:Nn \g_xpeek_ignorelist_tl { \xspace }
+% \end{IVerbatim}
+% (Note that this does not require \cs{xspace} to be defined;
+%  it merely ensure that
+%  the macro will work correctly
+%  if it is defined.)
+%
+% Next, we declare the new italicization command \cs{xit}.
+% It calls \cs{xpeek_collect_do:nn} to collect
+% any instances of \cs{xspace} that might follow the invocation.
+% These are saved, but ignored for the time being.
+%
+% It’s within the \Arg{code} argument to \cs{xpeek_collect_do:nn},
+% between \cs{group_begin:} and \cs{group_end:},
+% that the italicization happens.
+% Before we return to the default font, though,
+% the code checks whether \cs{l_peek_token} is in the ignore-list;
+% it then decides whether to include italic correction.
+%
+% Recall next that the last command within the \Arg{code}
+% is what will get the collected tokens as an argument.
+% Since we want to keep \cs{xspace} if it’s there,
+% we finish up the code with \cs{use:n},
+% which simply puts its argument into the input stream.
+%
+% Here’s the code:
+% \begin{IVerbatim}
+%   \NewDocumentCommand \xit {m}
+%     {
+%       \xpeek_collect_do:nn \g_xpeek_ignorelist_tl
+%         {
+%           \group_begin:
+%           \itshape #1
+%           \xpeek_if_in:NNTF \g_xpeek_matchlist_tl \l_peek_token
+%             { } { \/ }
+%           \group_end:
+%           \use:n
+%         }
+%     }
+% \end{IVerbatim}
+%
+% Finally, define the command we tried to make work in the introduction,
+% and see whether we’ve made it work:
+% \begin{IVerbatim}
+%   \NewDocumentCommand \naif {} {\xit{na\"\i f}\xspace}
+%   He was a true \naif.
+%   He was such a \naif he expected this example to work.
+% \end{IVerbatim}
+% The code above yields this result:
+% \begin{quote}
+%   \ExplSyntaxOn
+%   \tl_gset:Nn \g_xpeek_matchlist_tl  { , . }
+%   \tl_gset:Nn \g_xpeek_ignorelist_tl { \xspace }
+%   \NewDocumentCommand \xit {m}
+%     {
+%       \xpeek_collect_do:nn \g_xpeek_ignorelist_tl
+%         {
+%           \group_begin:
+%           \itshape #1
+%           \xpeek_if_in:NNTF \g_xpeek_matchlist_tl \l_peek_token
+%             { } { \/ }
+%           \group_end:
+%           \use:n
+%         }
+%     }
+%   \ExplSyntaxOff
+%   \NewDocumentCommand \naif {} {\xit{na\"\i f}\xspace}
+%   He was a true \naif.
+%   He was such a \naif he expected this example to work.
+% \end{quote}
+% And work it does.
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{\jobname} Implementation}
+%
+%    \begin{macrocode}
+%<*package>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<@@=xpeek>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\RequirePackage{expl3, xparse}
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+\ProvidesExplPackage
+  {xpeek} {2012/08/15} {0.2}
+  {Define commands that peek ahead in the input stream}
+%    \end{macrocode}
+%
+% \subsection{Match- \& Ignore-Lists}
+%
+% \begin{variable}{\g_xpeek_matchlist_tl, \g_xpeek_ignorelist_tl}
+%   Define the lists.
+%    \begin{macrocode}
+\tl_new:N \g_xpeek_matchlist_tl
+\tl_new:N \g_xpeek_ignorelist_tl
+%    \end{macrocode}
+% \end{variable}
+%
+% \subsection{Peek-ahead}
+%
+% \begin{macro}[internal]{\xpeek_after:nw}
+% \begin{variable}[aux]{\l_@@_code_tl}
+%   Define a function (AKA “token-list”) to hold the code passed in.
+%    \begin{macrocode}
+\tl_new:N \l_@@_code_tl
+%    \end{macrocode}
+%   Store the argument in \cs{l_@@_code_tl},
+%   then defer to \cs{peek_after:Nw}
+%    \begin{macrocode}
+\cs_new_protected:Npn \xpeek_after:nw #1
+  {
+    \tl_set:Nn \l_@@_code_tl {#1}
+    \peek_after:Nw \l_@@_code_tl
+  }
+%    \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+% \subsection{Collecting Ignored Tokens}
+%
+% \begin{macro}{\xpeek_collect_do:nn}
+% \begin{macro}[aux]{\@@_collect_do:nnn}
+% \begin{variable}[aux]{\l_@@_collected_tokens_tl}
+%   \begin{arguments}
+%     \item Tokens to collect from the input stream
+%     \item The operation to be performed on the collected tokens
+%     \item The next token on the input stream
+%           (for \cs{@@_collect_do:nnn}).
+%   \end{arguments}\noindent
+%   Define a list to save collected tokens in.
+%    \begin{macrocode}
+\tl_new:N \l_@@_collected_tokens_tl
+%    \end{macrocode}
+%   Clear list and defer to auxiliary function.
+%    \begin{macrocode}
+\cs_new_protected:Npn \xpeek_collect_do:nn #1#2
+  {
+    \tl_clear:N \l_@@_collected_tokens_tl
+    \@@_collect_do:nnn {#1} {#2} {}
+  }
+%    \end{macrocode}
+%   Recursively consume tokens from the input stream
+%   so long as they match the collect-list,
+%   then apply the operation to the collected tokens.
+%   The last matching token will not yet have been collected,
+%   so pass it to the operation as well.
+%   (If there were no matching tokens,
+%    |#3| will be the empty argument
+%    that was passed by \cs{xpeek_collect_do:nn}.)
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_collect_do:nnn #1#2#3
+  {
+    \xpeek_after:nw
+      {
+        \xpeek_if_in:NNTF #1 \l_peek_token
+          {
+            \tl_put_right:Nn \l_@@_collected_tokens_tl {#3}
+            \@@_collect_do:nnn {#1} {#2}
+          }
+          {
+            #2 { \l_@@_collected_tokens_tl #3 }
+          }
+      }
+  }
+%    \end{macrocode}
+% \end{variable}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Searching Through Token-Lists}
+%
+% \begin{macro}{\xpeek_if_in:NNTF}
+% \begin{variable}[aux]{\l_@@_bool}
+%   \begin{arguments}
+%     \item Token-list to search through (the “haystack”)
+%     \item Token to search for (the “needle”)
+%   \end{arguments}\noindent
+%   Define an internal flag.
+%    \begin{macrocode}
+\bool_new:N \l_@@_bool
+%    \end{macrocode}
+%    Map the “haystack”, searching for the “needle”.
+%    \begin{macrocode}
+\prg_new_protected_conditional:Npnn \xpeek_if_in:NN #1#2 { TF }
+  {
+    \bool_set_false:N \l_@@_bool
+    \tl_map_inline:Nn #1
+      {
+        \token_if_eq_charcode:NNT #2 ##1
+          {
+            \bool_set_true:N \l_@@_bool
+            \tl_map_break:
+          }
+      }
+    \bool_if:NTF \l_@@_bool
+      { \prg_return_true: } { \prg_return_false: }
+  }
+%    \end{macrocode}
+% \end{variable}
+% \end{macro}
+%
+%    \begin{macrocode}
+%</package>
+%    \end{macrocode}
+%
+% \section{Test Suite}
+%
+% \changes{Version 0.2}{2012/08/15}{Added test-suite}
+%
+% ^^A Reset line numbers in code listings
+% \makeatletter
+% \c@CodelineNo 0
+% \makeatother
+%
+% The test suite below is run automatically
+% when this document is produced.
+% It can also be run separately by executing
+% \begin{IVerbatim}[commandchars=+\[\]]
+%   latex +jobname-test
+% \end{IVerbatim}
+% at the command prompt.
+%
+% \input{\jobname.tests}
+%
+%    \begin{macrocode}
+%<testsuite>\documentclass{article}
+%<testsuite>\usepackage{xparse, expl3, xpeek, qstest}
+%<testsuite>\begin{document}
+%<*tests>
+%    \end{macrocode}
+%
+% \subsection{Set-Up: Wrapping \cs{Expect}}
+%
+% \begin{macro}[internal]{\ExpectIdenticalWidths}
+% Since the commands \pkg{xpeek} helps produce are not expandable,
+% directly comparing their outputs is not feasible.
+% Instead, typeset two versions into boxes
+% and have \pkg{qstest} compare these boxes’ widths.
+% (Thanks to Heiko Oberdiek for this technique;
+%  see \url{http://tex.stackexchange.com/q/67192/2966}.)
+%    \begin{macrocode}
+\ExplSyntaxOn
+\NewDocumentCommand \ExpectIdenticalWidths { m m }
+  {
+    \hbox_set:Nn \l_tmpa_box {#1}
+    \hbox_set:Nn \l_tmpb_box {#2}
+    \Expect
+      * {\dim_use:N \box_wd:N \l_tmpa_box}
+      * {\dim_use:N \box_wd:N \l_tmpb_box}
+  }
+\ExplSyntaxOff
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Emulating \cs{xspace}}
+%
+% Define a simple analogue to \cs{xspace}.
+%    \begin{macrocode}
+\ExplSyntaxOn
+\tl_const:Nn \c_xsp_exceptions_tl { ,;:.!? }
+\NewDocumentCommand \xsp {}
+  {
+    \xpeek_collect_do:nn \c_empty_tl
+      {
+        \xpeek_if_in:NNTF \c_xsp_exceptions_tl \l_peek_token
+          { } { ~ }
+      }
+  }
+\ExplSyntaxOff
+%    \end{macrocode}
+% Test \cs{xsp}, ensuring that it is space-factor–agnostic.
+%    \begin{macrocode}
+\begin{qstest}{Emulating \xspace}{xpeek}
+  \ExpectIdenticalWidths{foo bar}{foo\xsp bar}
+  \ExpectIdenticalWidths{foo. bar}{foo\xsp. bar}
+  \ExpectIdenticalWidths{FOO. bar}{FOO\xsp. bar}
+  \ExpectIdenticalWidths{foo. bar}{foo.\xsp bar}
+  \ExpectIdenticalWidths{FOO. bar}{FOO.\xsp bar}
+\end{qstest}
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%</tests>
+%<testsuite>\end{document}
+%    \end{macrocode}
+% N.B\@. The stand-alone test-suite will not produce any output,
+% only a log file.
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex
-- 
cgit v1.2.3

