summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/source/latex/xpeek
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2012-08-13 22:40:06 +0000
committerKarl Berry <karl@freefriends.org>2012-08-13 22:40:06 +0000
commit99f6ebc940e504927ea38d6600f8ca9f893667bb (patch)
tree3d5ad87dd691152e3f00d0bbb056f6933654ff1f /Master/texmf-dist/source/latex/xpeek
parentdd8cdebd361b58d83e12316d2f533932bfea8ae9 (diff)
new (semi-old) latex package xpeek (13aug12)
git-svn-id: svn://tug.org/texlive/trunk@27393 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/source/latex/xpeek')
-rw-r--r--Master/texmf-dist/source/latex/xpeek/xpeek.dtx582
1 files changed, 582 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/xpeek/xpeek.dtx b/Master/texmf-dist/source/latex/xpeek/xpeek.dtx
new file mode 100644
index 00000000000..5eca346e2bc
--- /dev/null
+++ b/Master/texmf-dist/source/latex/xpeek/xpeek.dtx
@@ -0,0 +1,582 @@
+% \iffalse meta-comment
+% !TeX program = pdflatex
+% !TeX encoding = utf-8
+% !TeX spellcheck = en_US
+%<*internal>
+\iffalse
+%</internal>
+%<*internal>
+\fi
+\def\nameofplainTeX{plain}
+\ifx\fmtname\nameofplainTeX\else
+ \expandafter\begingroup
+\fi
+%</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
+\postamble
+\endpostamble
+\usedir{tex/latex/xpeek}
+\generate{
+ \file{\jobname.sty}{\from{\jobname.dtx}{package}}
+}
+%</install>
+%<install>\endbatchfile
+%<*internal>
+\usedir{source/latex/xpeek}
+\generate{
+ \file{\jobname.ins}{\from{\jobname.dtx}{install}}
+}
+\ifx\fmtname\nameofplainTeX
+ \expandafter\endbatchfile
+\else
+ \expandafter\endgroup
+\fi
+%</internal>
+%<*driver>
+\pdfobjcompresslevel=0
+\RequirePackage[utf8]{inputenc}
+\documentclass[full]{l3doc}
+\usepackage{xpeek}
+\usepackage{xspace}
+\usepackage[all]{foreign}
+
+^^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 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,
+% \bool_new:N, \bool_set_true:N, \bool_set_false:N, \bool_if:NTF,
+% \cs_new_protected:Npn,
+% \peek_after:Nw,
+% \prg_new_protected_conditional:Npnn,
+% \prg_return_true:, \prg_return_false:,
+% \tl_new:N, \tl_clear:N, \tl_set:Nn,
+% \tl_put_right:Nn,
+% \tl_map_inline:Nn, \tl_map_break:,
+% \token_if_eq_charcode:NNT,
+% }
+%
+% \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}\centering
+% \begin{tabular}{lll}
+% |\textit{half}hearted| & \textit{half}hearted & (correct)\\
+% |{\itshape half}hearted| & {\itshape half}hearted & (overlap)\\
+% |{\itshape half\/}hearted| & {\itshape half\/}hearted & (correct)\\
+% ^^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 \textit{half}.| & by \textit{half\nocorr}. & (correct)\\
+% |by {\itshape half}.| & by {\itshape half}. & (correct)\\
+% |by {\itshape half\/}.| & by {\itshape half\/}. & (too wide)\\
+% \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{quote}
+% |\NewDocumentCommand \naif {} {\textit{na\"\i f}}|\\
+% …\\
+% |He was a true \naif.|
+% \end{quote}
+% 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{quote}
+% |He was such a \naif he expected this example to work.|
+% \end{quote}
+% 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{quote}
+% |\NewDocumentCommand \naif {} {\textit{na\"\i f}\xspace}|\\
+% …\\
+% |He was such a \naif he expected this example to work.|
+% \end{quote}
+% 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{quote}
+% |He was a true \naif.|
+% \end{quote}
+% 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} \marg{tokens}
+% \cs{tl_gremove_all:Nn} \cs{g_xpeek_\meta{list}_tl} \marg{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{quote}
+% |\NewDocumentCommand \naif {} {\textit{na\"\i f}\xspace}|
+% \end{quote}
+% 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} \marg{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 \marg{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} \marg{ignore-list} \marg{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{quote}
+% |\xpeek_collect_do:nn { abc } { \foo \bar } caada|
+% \end{quote}
+% 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{quote}
+% |\foo \bar { caa } da|
+% \end{quote}
+% (Note that it’s \cs{bar} \emph{within} the \marg{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}} \
+% \marg{true-code} \marg{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{quote}
+% |\xpeek_if_in:NNTF \g_xpeek_matchlist_tl \l_peek_token|\\
+% | |\marg{true-code} \marg{false-code}
+% \end{quote}
+% 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}.
+%
+% Set the match-list equal to \LaTeX’s default \cs{nocorrlist}.
+% Only \cs{xspace} is needed in the ignore-list:
+% \begin{quote}
+% |\tl_gset:Nn \g_xpeek_matchlist_tl { , . }|\\
+% |\tl_gset:Nn \g_xpeek_ignorelist_tl { \xspace }|
+% \end{quote}
+% (Note that this does not require \cs{xspace} to be defined;
+% it merely ensure that if it is defined,
+% the macro will work correctly.)
+%
+% 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.
+% The \marg{code} argument to \cs{xpeek_collect_do:nn}
+% is where the italicization happens.
+% Before that ends, though,
+% the code checks whether \cs{l_peek_token} is in the ignore-list
+% and includes italic correction, or not, appropriately.
+% The last command within the \marg{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.
+% \begin{quote}
+% |\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{quote}
+%
+% Finally, define the command we tried to make work in the introduction,
+% and see whether we’ve made it work.
+% \begin{quote}
+% |\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}
+% After all this, 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/09} {0.1}
+ {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}
+%
+% \end{implementation}
+%
+% \PrintChanges
+%
+% \PrintIndex