我在乳胶文档中有一些 mercurial 关键字,希望进行处理。
\documentclass[12pt]{article}
% This macro is an example based on the Tex FAQ:
% https://texfaq.org/FAQ-RCS
% We define \hgDate and \hgRevision macros.
\def\hg$#1: #2 ${\expandafter\def\csname hg#1\endcsname{#1: #2 }}
% This is what will be in the preamble if keyword expansion is turned on.
\hg$Revision: 123 $ % this is auto expanded by hg - do not change
\hg$Date: 2015-01-08 $ % this is auto expanded by hg - do not change
% But ... if keyword expansion is turned off or not set the above will
% be like the two lines below. Note: commented out as this does not work yet.
% \hg$Revision$
% \hg$Date$
\begin{document}
Minimal example of plain tex macro to process mercurial keyword expansions.
If \verb+\hg$Date$+ exists in the preamble then we wish the macro
\verb+\hgDate+ to display ``Date: None''. If \verb+\hg$Date: 2014-11-10 $+
exists in the preamble then we wish the macro \verb+\hgDate+ to display
``Date: 2014-11-10''. Similarly for \verb+\hg$Revision$+.
This doc is \hgRevision and \hgDate
\end{document}
上述代码适用于 $Date xx $ 和 $Revision 12 $,但不适用于 $Date$ 或 $Revision$,因为它没有第二个参数。
有 svn latex 包,但它们使用的关键字格式与 mercurial 不同,因此不适用于日期。我希望使用一个简单的 TeX 宏,因为我的 latex 文档被 Windows 和 Mac 用户使用,我只想在顶部添加一个简短的 tex 宏,而不是用户必须安装新的 latex 包。以下是我的一些明显错误的尝试。
这次尝试测试第二个参数是否为空:
\def\foo{}%
\def\hg$#1: #2 $%
{\expandafter\def\csname hg#1\endcsname%
{\if\foo#2\foo #1: None\else #1: #2\fi}%
}
%\hg$Revision$
\hg$Revision: 123 $
\hg$Date: 2015-01-08 $
\hgDate \hspace{2ex} \hgRevision
此尝试测试第二个参数是否为空:
\def\hg$#1: #2 ${\expandafter\def\csname hg#1\endcsname%
{#1: \def\temp{#2\unskip}%
\ifx\temp\empty
None
\else
#2
\fi}%
}
%\hg$Revision$
\hg$Revision: 123 $
\hg$Date: 2015-01-08 $
\hgDate \hspace{2ex} \hgRevision
这次尝试使用 detokenize 来查看参数 2 是否为空:
\def\hg$#1: #2 $%
{\expandafter\def\csname hg#1\endcsname%
{#1: \if\relax\detokenize{#2}\relax yy\else xx\fi}%
}
%\hg$Revision$
\hg$Revision: 123 $
\hg$Date: 2015-01-08 $
\hgDate \hspace{2ex} \hgRevision
如果没有第二个参数,则每个方法都会失败。也许我应该尝试删除 $ 或更改其 catcode?更可能的是,我需要更好地理解 TeX!
谢谢
答案1
我发现它的使用xparse
和expl3
功能很方便,可以避免复杂的情况。
\documentclass[12pt]{article}
\usepackage{xparse}
\NewDocumentCommand{\hg}{>{\SplitArgument{1}{:}}r$$}
{%
\processhg#1%
}
\ExplSyntaxOn
\NewDocumentCommand{\processhg}{mm}
{
\IfNoValueTF{#2}
{\hg_define_hg:nn { #1 } { None } }
{\hg_define_hg:nn { #1 } { #2 } }
}
\cs_new_protected:Nn \hg_define_hg:nn
{
\cs_gset:cpx {hg#1}{ \tl_trim_spaces:n { #2 } }
}
\ExplSyntaxOff
% This is what will be in the preamble if keyword expansion is turned on.
\hg$Revision: 123 $ % this is auto expanded by hg - do not change
\hg$Date: 2015-01-08 $ % this is auto expanded by hg - do not change
\begin{document}
Revision: \hgRevision; Date: \hgDate.
Now we emulate calling the macros without parameters
\hg$Revision$
\hg$Date$
Revision: \hgRevision; Date: \hgDate.
\end{document}
该宏\hg
有一个由$
符号分隔的参数。该参数在冒号处被分成两部分,但如果没有找到冒号,则第二个参数将接收一个特殊值,使测试\IfNoValueTF
为真。
拆分参数仍由 表示,#1
并传递给第二个宏进行处理。这反过来会使用适当的参数(None
如果没有冒号)调用内部函数。
答案2
如果我不采用任何包装,这将是以下风格的东西。它假设类似于$name$
或$name:<space>stuff<space>$
。如果空格是可选的,则代码必须进行相应的调整,这并不难(对于TeX
精通 宏语言的人来说!)。
\documentclass[12pt]{article}
\makeatletter
\def\hg #1{\def\hg $##1${\hg@ ##1:#1#1:\hg@}}
\hg { }
\def\hg@ #1: #2 :#3\hg@{\if\relax\detokenize{#2}\relax
\@namedef{hg#1}{#1: None}%
\else
\@namedef{hg#1}{#1: #2}%
\fi
}
\makeatother
% This is what will be in the preamble if keyword expansion is turned on.
\hg$Revision: 123 $ % this is auto expanded by hg - do not change
\hg$Date: 2015-01-08 $ % this is auto expanded by hg - do not change
% \hg$Revision$
% \hg$Date$
\begin{document}
Minimal example of plain tex macro to process mercurial keyword expansions.
If \verb+\hg$Date$+ exists in the preamble then we wish the macro
\verb+\hgDate+ to display ``Date: None''. If \verb+\hg$Date: 2014-11-10 $+
exists in the preamble then we wish the macro \verb+\hgDate+ to display
``Date: 2014-11-10''. Similarly for \verb+\hg$Revision$+.
This doc is \hgRevision{} and \hgDate.
\hg$Revision$
\hg$Date$
This doc is \hgRevision{} and \hgDate.
\end{document}
答案3
如果输入模式是 或 ,则
\hg$⟨name⟩$
\hg$⟨name⟩:⟨space⟩⟨stuff⟩⟨space⟩$
- ⟨姓名⟩不包含以空格结尾的冒号(序列
:
)并且 - ⟨东西⟩不包含以冒号结尾的空格(序列
:
)
,您可以尝试使用分隔参数。
但请注意,在 之间的所有内容$...$
和 的其他所有内容都用一层花括号括起来⟨姓名⟩其余部分用一层花括号括起来⟨东西⟩如果存在的话,将会被剥离。
\documentclass[12pt]{article}
\makeatletter
\def\hg $#1${\hg@ #1: {None/Default} :\hg@}
\def\hg@ #1: #2 :#3\hg@{\@namedef{hg#1}{#1: #2}}
\makeatother
\begin{document}
Calls to \verb|\hgRevision|/\verb|\hgDate| are nested between parentheses so you can see if spaces slipped in.
\bigskip
\par \textbf{Revision/Date empty:}
\hg$Revision$
\hg$Date$
\par (\hgRevision)
\par (\hgDate)
\par \textbf{Without whatsoever surrounding curly braces:}
\hg$Revision: 123 $
\hg$Date: 2015-01-08 $
\par (\hgRevision)
\par (\hgDate)
\par \textbf{With surrounding curly braces that get stripped off:}
\hg${{Revision}: {123} }$
\hg${{Date}: {2015-01-08} }$
\par (\hgRevision)
\par (\hgDate)
\end{document}
如果由于某种原因您希望防止剥离括号,则可以拆分机制,以便每个分隔参数在前面添加某些内容后由另一个宏处理:
\documentclass[12pt]{article}
\makeatletter
\def\hg${\hg@.}
\def\hg@#1${\hg@@#1: {None/Default} :\hg@@}
%\def\hg@#1${\hg@@#1: None/Default :\hg@@}
\def\hg@@#1: {\expandafter\hg@@i\expandafter{\hg@@gobbledot#1}.}
\def\hg@@i#1#2 :{\expandafter\hg@@ii\expandafter{\hg@@gobbledot#2}{#1}}
\def\hg@@ii#1#2#3\hg@@{\@namedef{hg#2}{#2: #1}}
\def\hg@@gobbledot.{}
\makeatother
\begin{document}
Calls to \verb|\hgRevision|/\verb|\hgDate| are nested between parentheses so you can see if spaces slipped in.
\bigskip
\par \textbf{Revision/Date empty:}
\hg$Revision$
\hg$Date$
\par (\hgRevision)
\par (\hgDate)
\par {\tt\frenchspacing\string\hgRevision=\meaning\hgRevision}
\par {\tt\frenchspacing\string\hgDate=\meaning\hgDate}
\par \textbf{Without whatsoever surrounding curly braces:}
\hg$Revision: 123 $
\hg$Date: 2015-01-08 $
\par (\hgRevision)
\par (\hgDate)
\par {\tt\frenchspacing\string\hgRevision=\meaning\hgRevision}
\par {\tt\frenchspacing\string\hgDate=\meaning\hgDate}
\par \textbf{With surrounding curly braces that do not get stripped off:}
\hg$Revision: {123} $
\hg$Date: {2015-01-08} $
\par (\hgRevision)
\par (\hgDate)
\par {\tt\frenchspacing\string\hgRevision=\meaning\hgRevision}
\par {\tt\frenchspacing\string\hgDate=\meaning\hgDate}
\hg${Revision}: {123} $
\hg${Date}: {2015-01-08} $
\par (\csname hg{Revision}\endcsname)
\par (\csname hg{Date}\endcsname)
\par {\tt\frenchspacing\expandafter\string\csname hg{Revision}\endcsname=\expandafter\meaning\csname hg{Revision}\endcsname}
\par {\tt\frenchspacing\expandafter\string\csname hg{Date}\endcsname=\expandafter\meaning\csname hg{Date}\endcsname}
\hg${{Revision}: {123} }$
\hg${{Date}: {2015-01-08} }$
\par (\csname hg{{Revision}: {123} }\endcsname)
\par (\csname hg{{Date}: {2015-01-08} }\endcsname)
\par {\tt\frenchspacing\expandafter\string\csname hg{{Revision}: {123} }\endcsname=\expandafter\meaning\csname hg{{Revision}: {123} }\endcsname}
\par {\tt\frenchspacing\expandafter\string\csname hg{{Date}: {2015-01-08} }\endcsname=\expandafter\meaning\csname hg{{Date}: {2015-01-08} }\endcsname}
\end{document}