I am modifiyng some macros to enforce a section style that should end with a full stop. The full stop is added automatically, but sometimes people add the full stop and I get two. How can I modify the code to detect the punctuation and add it only if it is needed? I guess I need to use futurelet, but I don't know how to use it.
2 Answers
Firstly it is always best to post a MWE. As you have not posted one I am posting a general answer, for exact formatting of the section you will need to add your own formatting commands.
You don't need a \futurelet
. TeX keeps track of special codes for letters and punctuation to place the right amount of space after a stop. We can check the value of the \spacefactor
macro and add a stop or do nothing if it is already there.
\newcommand\@addpunctuation[1]{\ifnum\spacefactor>1000\else#1\fi}
The rest of the code are minor details. Here is the MWE.
\documentclass{article}
\makeatletter
\newcommand\@addpunctuation[1]{\ifnum\spacefactor>1000\else#1\fi}
\newcommand\buildhead[1]{#1\@addpunctuation.}
\DeclareRobustCommand{\addhead}[1]{\buildhead{#1}}
\DeclareRobustCommand\Section[1]{\addhead{#1}}
\def\section{\secdef \starcmd \unstarcmd}
\def\starcmd[#1]#2{\bfseries\Large\addhead{#2}}
\newcommand\unstarcmd[1]{\bfseries\setcounter{section}{O}\renewcommand\thesection{\Alph{section} \addhead{#1}}}
\makeatother
\parindent0pt
\begin{document}
\makeatletter
\section[small title]{Testing}\\
\section{Testing}\\ % remains as is
\Section{Testing}\\
\Section{Testing.}
\end{document}
If you want the macro to work both for \frenchspacing
and \nonfrenchspacing
, you can try:
\newcommand\@addpunctuation[1]{%
\nonfrenchspacing
\ifnum\sfcode`.=1000
\ifnum\spacefactor>1000\else#1\fi
\else
\ifnum\spacefactor>1000\else#1\fi
\nonfrenchspacing
\fi
}
and test with
\frenchspacing
Test. \fbox{A test test}
\nonfrenchspacing
Test. \fbox{A test test}
You can also visit the link quoted by Philippe Goutet in the comments below.
-
-
@morbusg
\frenchspacing
sets all thesfcodes
to 1000 so one would have to modify the above to check forfrenchspacing
. Perhaps a better technique is to check the value of thesfcode
for the stop and then do the check.– yannislMar 2, 2012 at 6:23 -
1@morbusg: see \frenchspacing not setting the space factor codes correctly? for how the ams solved the problem with its
\@addpunct
macro. Mar 2, 2012 at 10:56 -
@PhilippeGoutet Thanks for the link. I posted an alternative in the answer which I had already worked out, would you please care to check it out and comment on it?– yannislMar 2, 2012 at 11:09
-
@YiannisLazarides: Using your
\@addpunctuation
macro with\frenchspacing
, I get two dots. The problem with the idea of starting\@addpunctuation
with\nonfrenchspacing
is that the space factor has already been set, so it's too late as\frenchspacing Text.\nonfrenchspacing\the\spacefactor
will show. I don't think you can get around redefining\frenchspacing
as the AMS did. Mar 2, 2012 at 13:26
The problem can be solved at a macro level without 'meddling' with the space factor.
\documentclass{article}
\makeatletter
\usepackage{catoptions}
\robust@def*\newsection#1{%
\begingroup
\edef\currtitle{\cpttrimspaces{#1}}%
\def\currlabel{}\def\lasttok{}%
\xifinsetFT{\detokenize{\label}}{\cptoxdetok\currtitle}{}{%
\def\reserved@a##1\label##2##3\cpt@nil{%
\def\currtitle{##1##3}%
\def\currlabel{\noexpand\label{##2}}%
}%
\expandafter\reserved@a\currtitle\cpt@nil
}%
\def\getlasttok##1{%
\expandafter\ifx\@car##1\@nil\cpt@nnil
\else
\edef\lasttok{\unexpanded{##1}}%
\expandafter\getlasttok
\fi
}%
\expandafter\getlasttok\currtitle\cpt@nnil
\s@expandarg\ifinsetTF\lasttok{\dots\ldots\textellipsis}{%
\@tempswatrue
}{%
\begingroup
\lccode`\X=133
\lowercase{\endgroup\expandafter\expandafter\expandafter
\ifstrcmpTF\expandafter\@car\lasttok\@nil X}{%
\@tempswatrue
}{%
\@tempswafalse\let\elt\relax
\def\siso@do##1{%
\xifinsetFT{\detokenize{##1}\elt}{\cptoxdetok\currtitle\elt}{}{%
\@tempswatrue\loopbreak
}%
}%
\siso@@loop{.!?:\dots\ldots\textellipsis}%
}%
}%
\def\elt{\expandcsonce\currtitle}%
\cptexpandarg{\endgroup\section}{\if@tempswa\elt\else\elt.\fi\currlabel}%
}
\makeatother
\begin{document}
\newsection{Test section.\label{sec:1}}
As seen later in Section~\ref{sec:this} \ldots.
\newsection{Test section}
\newsection{Test section.}
\newsection{Is this a test section?}
\newsection{Heh, this is test section!}
\newsection{Test section\dots}
\newsection{Test section\ldots}
\newsection{Test section\textellipsis}
\newsection{Test section\label{sec:this}\ldots}
As seen earlier in Section~\ref{sec:1} \ldots.
% I have accounted for the ASCII character 133 but you may not get it to print:
\newsection{Test section …}
\end{document}
-
This will not work with
\dots
:\newsection{Test section\dots}
will give four dots instead of three Mar 3, 2012 at 7:48 -
@PhilippeGoutet: Please do you have other failure modes, so that I can try to address them at once? Mar 3, 2012 at 14:50
-
Well, it should work with all the punctuation so it is safer to also add
,
and:
and;
but that should be all. Of course, the solution should also work with\ldots
,\textellipsis
or other\dots
variants as well as the symbol…
(in unicode, U+2026). Mar 3, 2012 at 16:05 -
@PhilippeGoutet: I haven't seen section and paragraph headings ending with comma (,) or semicolon (;). So I think we should leave them out. Mar 3, 2012 at 17:03
-
Well, for inline sections, you could imagine things like
\paragraph{Start of the title,} end of the title.
(I've done this myself a few times). Anyway, as a comma or a semi-colon should never be followed by a full stop, just as well add them, it's safer. You never know what can cross the mind of your end users. Mar 3, 2012 at 18:54
\documentclass
and the appropriate packages so that those trying to help don't have to recreate it. This will also clarify any ambiguities in your question.