Latex 自定义命令的目录

Latex 自定义命令的目录

我有一个自定义命令:

\register{address}{format}{shortdescription}{longdescription}

我现在想创建此命令的目录,但不是以正常方式,它应该是包含一些命令参数的真实表。

Address || shortdescription || format || reflink

这可能吗?我可以使用什么包来实现这一点?基本上,我希望有一个包含所有\register已创建对象的概览表。我找到了tocloft但没有看到访问监视命令的参数的选项。

答案1

\addtocontents/ \@starttocLaTeX 2ε-kernel 可能是你的朋友:

\documentclass{article}
\usepackage{longtable}
\makeatletter
\newcommand\register[4]{%
  \@bsphack
  \addtocontents{rgs}{\string\registerline{#1}{#2}{#3}{#4}\protected@file@percent}%
  \@esphack
}%
\AtEndDocument{\addtocontents{rgs}{\string\endinput}}%
\newcommand\registertable{%
  \section*{Table of...}%
  \IfFileExists{\jobname.rgs}{%
    \begin{longtable}[l]{|c|c|c|c|}%
    \hline Address&shortdescription&format&reflink/long\\\hline\endhead
    \hline\endfoot
    \@@input\jobname.rgs
    \end{longtable}%
  }{}%
  \begingroup
  \def\@input##1{}%
  \@starttoc{rgs}%
  \endgroup
  \@nobreaktrue
}%
\newcommand\registerline[4]{#1&#3&#2&#4\\}%
\makeatother

\begin{document}
\registertable
Some text
\register{Address 1}{Format 1}{Short 1}{Long 1}
\register{Address 2}{Format 2}{Short 2}{Long 2}
\register{Address 3}{Format 3}{Short 3}{Long 3}
\register{Address 4}{Format 4}{Short 4}{Long 4}

Some text
\register{Address 5}{Format 5}{Short 5}{Long 5}
\register{Address 6}{Format 6}{Short 6}{Long 6}
\register{Address 7}{Format 7}{Short 7}{Long 7}
\register{Address 8}{Format 8}{Short 8}{Long 8}
\register{Address 9}{Format 9}{Short 9}{Long 9}
\register{Address 10}{Format 10}{Short 10}{Long 10}
\register{Address 11}{Format 11}{Short 11}{Long 11}
\end{document}

在此处输入图片描述


或者,您可以将包数据工具与文档范围的 l3 属性列表结合使用,并将属性值保存到数据工具数据库中,该数据库在 latex 运行结束时将写入外部逗号分隔值文件,从中检索要显示的内容。

(实际上使用了两个数据库 - 一个用于获取源自前一次 latex 运行的数据,一个用于存储当前 latex 运行期间的数据并在 latex 运行结束时写入 .csv 文件。)

\documentclass{article}
\usepackage{datatool}
\usepackage{atveryend}
\usepackage{longtable}

\makeatletter
%========================================================================
%  \immediategroup{<balanced text>}%
%------------------------------------------------------------------------
%  A local scope wherein <balanced text> is carried out while \write, 
%  \closeout and \openout are patched to work in terms of \immediate.
%------------------------------------------------------------------------
\newcommand\immediategroup[1]{%
  \begingroup
  \@ifdefinable\MyStuffsavedwrite{\let\MyStuffsavedwrite\write}%
  \@ifdefinable\MyStuffsavedopenout{\let\MyStuffsavedopenout\openout}%
  \@ifdefinable\MyStuffsavedcloseout{\let\MyStuffsavedcloseout\closeout}%
  \def\write{\immediate\MyStuffsavedwrite}%
  \def\openout{\immediate\MyStuffsavedopenout}%
  \def\closeout{\immediate\MyStuffsavedcloseout}%
  #1%
  \endgroup
}%
%========================================================================
%  \DATABASEFILECreateIfNotExistent{<base-name of databases and of .csv-file>}%
%                                  {<separator>}%
%                                  {<delimiter>}%
% - Creates database of name: <base-name of databases and of .csv-file>DataFromPreviousLaTeXRun
% - Creates database of name: <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
%   These databases exist in TeX's memory during the LaTeX-run.
% - Triggers the writing of file  <base-name of databases and of .csv-file>.csv 
%   at the end of the LaTeX-run from the content of the database 
%   <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
% - Initializes Counter-macro \<base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRunCnt
%   to 0.   That counter-macro is to hold the value of the field "PrimaryKey" of the data-base 
%   <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun 
%   and is incremented always right before writing another line/row to the that database.
%
% The database <base-name of databases and of .csv-file>DataFromPreviousLaTeXRun is
% used for retrieving data. In case the file  <base-name of databases and of .csv-file>.csv 
% exists that database is initialized by reading that file. Otherwise it is
% initialized empty.It is not modified during the LaTeX-run.
% The data stems from the previous LaTeX-run.
%
% The <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun is
% used for writing/storing entries during the LaTeX-run. It is initialized empty.
% It is modified during the LaTeX-run. At the end of the LaTeX-run it is saved as
% file <base-name of databases and of .csv-file>.csv .
%
% At the end of the LaTeX-run both databases are compared. If they differ, then
% something has changed and you are informed that you need to do another LaTeX-run.
%
% <separator> is the separator of values in the .csv-file. Usually this is comma (,)
% , but in your question you specified that you wish to use semicolon (;).
%
% <delimiter>: In case a value contains <separator> it needs to be nested
% between two instances of <delimiter>. <delimiter> usually is the quotes-character (").
%------------------------------------------------------------------------
\newcommand\DATABASEFILECreateIfNotExistent[3]{%
  \DTLifdbexists{#1DataFromPreviousLaTeXRun}{}{%
    \begingroup
    \DTLsetseparator{#2}%
    \DTLsetdelimiter{#3}%
    \IfFileExists{#1.csv}{%
      \DTLloaddb{#1DataFromPreviousLaTeXRun}{#1.csv}%
    }{%
      \DTLgnewdb{#1DataFromPreviousLaTeXRun}%
    }%
    \endgroup
  }%
  \DTLifdbexists{#1DataToBeWrittenAtEndOfThisLaTeXRun}{}{%
    \DTLgnewdb{#1DataToBeWrittenAtEndOfThisLaTeXRun}%
    % --- Counter-macro for primary-key of database ---
    \expandafter\@ifdefinable\csname#1DataToBeWrittenAtEndOfThisLaTeXRunCnt\endcsname{%
      \expandafter\long\expandafter\gdef\csname#1DataToBeWrittenAtEndOfThisLaTeXRunCnt\endcsname{0}%
    }%
    % -------------------------------------------------
    \AfterLastShipout{%
      % This will save the ..DataToBeWrittenAtEndOfThisLaTeXRun-
      % database to file after the last \shipout. The last \shipout 
      % is triggered by \end{document}/\enddocument.
      \immediategroup{%
        \DTLsetseparator{#2}%
        \DTLsetdelimiter{#3}%
        \DTLsavedb{#1DataToBeWrittenAtEndOfThisLaTeXRun}{#1.csv}%
      }%
      % Check if content of database-token-register stemming from
      % database-file of previous LaTeX-run differs from content of
      % database-token-register stemming from database-file of 
      % the current LaTeX-run.
      % If so, something changed and you need to re-run LaTeX.
      % Probably this should be omitted with large databases.
      \begingroup
      \DTLsetseparator{#2}%
      \DTLsetdelimiter{#3}%
      \DTLgcleardb{#1DataToBeWrittenAtEndOfThisLaTeXRun}%
      \DTLnewdbonloadfalse
      \DTLloaddb{#1DataToBeWrittenAtEndOfThisLaTeXRun}{#1.csv}%
      \edef\tempa{%
        \the\csname dtldb@#1DataFromPreviousLaTeXRun\endcsname
        \the\csname dtlkeys@#1DataFromPreviousLaTeXRun\endcsname
        \the\csname dtlrows@#1DataFromPreviousLaTeXRun\endcsname
        \the\csname dtlcols@#1DataFromPreviousLaTeXRun\endcsname
      }%
      \edef\tempb{%
        \the\csname dtldb@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
        \the\csname dtlkeys@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
        \the\csname dtlrows@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
        \the\csname dtlcols@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
      }%
      \ifx\tempa\tempb
        \endgroup
      \else
        \endgroup
        \@latex@warning@no@line{%
           Database `#1' may have changed.
           Rerun to get listings of entries etc right%
         }%
      \fi
    }%
  }%
}%
%=========================================================================
% Infrastructure for triggering a warning in case the value of a document-
% wide property was requested at a moment in time when the property didn't
% exist (yet).
%-------------------------------------------------------------------------
\AfterLastShipout{\PropertyValuesNotFound}%
\newcommand*\PropertyValuesNotFound{}%
\newcommand*\PropertyValuesNotFoundTrue{%
  \gdef\PropertyValuesNotFound{\@latex@warning@no@line {There were undefined properties}}%
}%
\ExplSyntaxOn
%=========================================================================
% Property-List for properties that are to be maintained document-wide
% during the LaTeX-run. Values can change during the LaTeX-run.
% If you want to store the values which some properties have at a specific
% moment in time during the LaTeX-run, you can use the macro
% \DatabaseRowFromSomeOfThisDocumentsProperties
% for storing these properties' current values as another row of a
% database of name <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
%-------------------------------------------------------------------------
\prop_new:N {\g__MyStuff_ThisDocumentsProperties_prop}
%=========================================================================
% expl3-scratch-variables:
%-------------------------------------------------------------------------
\prop_gput:Nxx \g__MyStuff_ThisDocumentsProperties_prop{Primary-Key}{0}
\tl_new:N {\l__MyStuff_ExtractedProperty_tl}
\clist_new:N{\l__MyStuff_PropertyNames_clist}
\bool_new:N \g__MyStuff_NewDatabaseRow_bool
\cs_new:Nn \__MyStuff_ExpandedDatabaseName: {}
%=========================================================================
% Set document-wide properties' values _globally_ by providing a
% <property1>=<value1>, <property2>=<value2>, ..., <propertyN>=<valueN>-list:
% \SetSomeOfThisDocumentsPropertiesFromKeyValList{%
%    <property1>=<value1>, <property2>=<value2>, ..., <propertyN>=<valueN>
% }%
%
% If a property doesn't exist it is created anew automatically.
% If a property does exist, its value is overridden.
%-------------------------------------------------------------------------
\NewDocumentCommand{\SetSomeOfThisDocumentsPropertiesFromKeyValList}{m}{
  \keyval_parse:NNn \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:n
                    \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:nn
                    {#1}
}
\cs_new:Nn \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:n { 
  \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:nn {#1}{\DTLstringnull}
}
\cs_new:Nn \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:nn {
  \group_begin:
  \cs_set:Npn \protect { \noexpand\protect\noexpand }
  \prop_gput:Nxx \g__MyStuff_ThisDocumentsProperties_prop{#1}{#2}
  \group_end:
} 
%=========================================================================
% Create a row of a database 
% <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
% from current values of document-wide properties by specifying a comma-
% separated list of property-names.
% Names of properties will be used as names of fields/columns of the database.
% If a field/column doesn't yet exist in the database, it will be created automatically.
% If the database itself doesn't exist, it will be created automatically
% by \DATABASEFILECreateIfNotExistent. (That's why you nee to specify
% <separator> and <delimiter>.)
% If a property doesn't exist, an error-message is raised and datatool's
% null-value for strings is provided as value to the database.
%
%  \DatabaseRowFromSomeOfThisDocumentsProperties{<base-name of databases and of .csv-file>}%
%                                               {<separator>}%
%                                               {<delimiter>}%
%                                               {<Property1>, <Property2>,..., <PropertyN>}%
%
%-------------------------------------------------------------------------
\NewDocumentCommand{\DatabaseRowFromSomeOfThisDocumentsProperties}{mmmm}{
  \bool_gset_true:N \g__MyStuff_NewDatabaseRow_bool
  \cs_set:Nx \__MyStuff_ExpandedDatabaseName: {#1DataToBeWrittenAtEndOfThisLaTeXRun}
  \exp_args:Nx  \DATABASEFILECreateIfNotExistent{#1}{#2}{#3}
  \clist_set:Nn \l__MyStuff_PropertyNames_clist {#4}
  \cs_gset:cpx {\__MyStuff_ExpandedDatabaseName: Cnt} 
               {\number\numexpr\use:c{\__MyStuff_ExpandedDatabaseName: Cnt}+1\relax} 
  \prop_gput:Nxx \g__MyStuff_ThisDocumentsProperties_prop {PrimaryKey} {\use:c{\__MyStuff_ExpandedDatabaseName: Cnt}}
  \__MyStuff_DatabaseEntryFromThisDocumentsProperty:n {PrimaryKey}
  \clist_map_function:NN \l__MyStuff_PropertyNames_clist \__MyStuff_DatabaseEntryFromThisDocumentsProperty:x
}
\cs_new:Nn \__MyStuff_DatabaseEntryFromThisDocumentsProperty:n {
  \prop_get:NnN \g__MyStuff_ThisDocumentsProperties_prop {#1} \l__MyStuff_ExtractedProperty_tl
  \exp_args:NV \quark_if_no_value:nTF \l__MyStuff_ExtractedProperty_tl {
    \protect\PropertyValuesNotFoundTrue
    % I am too lazy to delve into expl3's l3msg
    \@latex@warning{Macro~ \token_to_str:N\DatabaseRowFromSomeOfThisDocumentsProperties:\space
    Property~`#1'~undefined~-~using~value~\DTLstringnull\space instead~-}
    \tl_set:Nn \l__MyStuff_ExtractedProperty_tl {\DTLstringnull}
  }{}
  \__MyStuff_DatabaseRowFromPropertyAndValue:onV \__MyStuff_ExpandedDatabaseName: {#1} \l__MyStuff_ExtractedProperty_tl
}
\cs_generate_variant:Nn \__MyStuff_DatabaseEntryFromThisDocumentsProperty:n {x}
\cs_new:Nn \__MyStuff_DatabaseRowFromPropertyAndValue:nnn {
  \DTLifhaskey{#1}{#2}{}{
    \DTLaddcolumn{#1}{#2}
  }
  \bool_if:NTF \g__MyStuff_NewDatabaseRow_bool{
     \DTLnewrow{#1}
  }{}
  \bool_gset_false:N \g__MyStuff_NewDatabaseRow_bool
  \group_begin:
  %\cs_set:Npn \protect { \noexpand\protect\noexpand }
  \cs_set:Npn \protect { \token_to_str:N  }
  \exp_args:Nnx \use:n {\DTLnewdbentry{#1}{#2}} {#3}
  \group_end:
}
\cs_generate_variant:Nn \__MyStuff_DatabaseRowFromPropertyAndValue:nnn{onV}
%=========================================================================
% Retrieve the current value of a document-wide property.
%
%  \GetDocumentsPropertyValue{<Property>}
%
% If a property doesn't exist, an error-message is raised and the
% tokens  \textsf{??}  are provided.
%
% Not used in the following code. But I wasn't aware of that at the time
% of composing all this. Probably it may be useful in other scenarios.
%-------------------------------------------------------------------------
\NewDocumentCommand{\GetDocumentsPropertyValue}{m}{
  \__GetDocumentsPropertyValue:x{#1}
}
\cs_new:Nn \__GetDocumentsPropertyValue:n {
  \prop_get:NnN \g__MyStuff_ThisDocumentsProperties_prop {#1} \l__MyStuff_ExtractedProperty_tl
  \exp_args:NV \quark_if_no_value:nTF \l__MyStuff_ExtractedProperty_tl {
    \protect\PropertyValuesNotFoundTrue
    % I am too lazy to delve into expl3's l3msg
    \@latex@warning{Macro~\token_to_str:N\GetDocumentsPropertyValue:\space
    Property~`#1'~undefined~-~using~value~\token_to_str:N\textsf{??}~instead~-}
    \tl_set:Nn \l__MyStuff_ExtractedProperty_tl {\textsf{??}}
  }{}
  \tl_use:N \l__MyStuff_ExtractedProperty_tl
}
\cs_generate_variant:Nn \__GetDocumentsPropertyValue:n {x}
%=========================================================================
%  Via datatool-package's `\DTLforeach` iterate on the rows
%  of the database  <base-name of databases and of .csv-file>DataFromPreviousLaTeXRun.
%
%  If the database itself doesn't exist, it will be created automatically
%  by \DATABASEFILECreateIfNotExistent. (That's why you nee to specify
%  <separator> and <delimiter>.)
%
% \DoWithDatabaseProperties{<base-name of databases and of .csv-file>}%
%                          {<separator>}%
%                          {<delimiter>}%
%                          [<condition>]%
%                          {<assign list>}%
%                          {<text>}%
%
% <base-name of databases and of .csv-file>, <separator> and <delimiter> are
% the same as above. 
% <condition>, <assign list> and <text> are the same as with \DTLforeach
% of the datatool-package.
%-------------------------------------------------------------------------
\NewDocumentCommand\DoWithDatabaseProperties{mmmo}{
  \exp_args:Nx \DATABASEFILECreateIfNotExistent {#1}{#2}{#3}
  \IfNoValueTF{#4}{\exp_args:Nnx \use:n {\DTLforeach*}}
                  {\exp_args:Nnx \use:n {\DTLforeach*[{#4}]}}
                  {#1DataFromPreviousLaTeXRun}
}
\ExplSyntaxOff
\makeatother
%=========================================================================
%  Now the individual building blocks of the infrastructure for managing 
%  document-wide properties via databases and .csv files are in place. 
%  They can now be used for implementing the macros at user level, which
%  is straightforward:
%=========================================================================
% \ListOf...-commands can easily be implemented in terms of
% \DoWithDatabaseProperties.
%
% A scratch-switch which can be initialized by the \ListOf...-command
% and toggled by \DoWithDatabaseProperties's <text>-argument. This 
% switch is intended to indicate whether at least one row of the 
% database already ended up as an entry of the list.
% 
% E.g., only in this case a heading for the list in question is needed.
% Only in this case starting/ending some environment is needed, e.g., 
% description or tabular.
%
\newif\ifNoDatabaseEntryEndedUpInTheListYet
\global\NoDatabaseEntryEndedUpInTheListYettrue
%=========================================================================
% An example of how to implement \registertable by means of
% \DoWithDatabaseProperties (whose underlying macro is \DTLforeach):
%-------------------------------------------------------------------------
\newtoks\scratchtoks
\newcommand\registertable{%
  \global\NoDatabaseEntryEndedUpInTheListYettrue
  \global\scratchtoks{}%
  \DoWithDatabaseProperties{register}{,}{"}%
                           {%
                              \registeraddress=address,%
                              \registerformat=format,%
                              \registershortdescription=shortdescription,%
                              \registerlongdescription=longdescription%
                           }%
                           {%
                             \ifNoDatabaseEntryEndedUpInTheListYet
                                \global\NoDatabaseEntryEndedUpInTheListYetfalse
                             \fi
                             \global\scratchtoks\expandafter{\the\expandafter\scratchtoks
                               \expanded{%
                                 \registeraddress&\registershortdescription&\registerformat&\registerlongdescription
                               }\\%
                             }%
                           }%
  \ifNoDatabaseEntryEndedUpInTheListYet\else
    \section*{Table of ...}%
    \begin{longtable}[l]{|c|c|c|c|}%
    \hline Address&shortdescription&format&reflink/long\\\hline\endhead
    \hline\endfoot
    \the\scratchtoks
    \end{longtable}%
  \fi
}%


\begin{document}

\registertable

\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 1, format=Format 1,  shortdescription=Short 1, longdescription=Long 1
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 2, format=Format 2,  shortdescription=Short 2, longdescription=Long 2
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 3, format=Format 3,  shortdescription=Short 3, longdescription=Long 3
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 4, format=Format 4,  shortdescription=Short 4, longdescription=Long 4
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 5, format=Format 5,  shortdescription=Short 5, longdescription=Long 5
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 6, format=Format 6,  shortdescription=Short 6, longdescription=Long 6
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 7, format=Format 7,  shortdescription=Short 7, longdescription=Long 7
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 8, format=Format 8,  shortdescription=Short 8, longdescription=Long 8
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 9, format=Format 9,  shortdescription=Short 9, longdescription=Long 9
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 10, format=Format 10,  shortdescription=Short 10, longdescription=Long 10
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
   address=Address 11, format=Format 11,  shortdescription=Short 11, longdescription=Long 11
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
   address, format, shortdescription, longdescription
}%

Some text

Some text

\end{document}

在此处输入图片描述

答案2

我认为,您需要一个新的“...列表”。阅读手册tocloft以了解如何执行此操作。您的\register宏需要将适当的信息添加到列表中...

然而,也许您正在考虑某种表格结构,在这种情况下,我会将其留给您决定。

相关内容