From 1598205ade1602e324b7fd8e5410d1389eae7416 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 12 Oct 2023 03:04:15 +0000 Subject: CTAN sync 202310120304 --- macros/generic/advice/INSTALL.md | 69 +++ macros/generic/advice/LICENCE | 416 ++++++++++++++ macros/generic/advice/Makefile | 8 + macros/generic/advice/README.md | 8 + macros/generic/advice/advice.edtx | 1095 +++++++++++++++++++++++++++++++++++++ macros/generic/advice/advice.ins | 33 ++ 6 files changed, 1629 insertions(+) create mode 100644 macros/generic/advice/INSTALL.md create mode 100644 macros/generic/advice/LICENCE create mode 100644 macros/generic/advice/Makefile create mode 100644 macros/generic/advice/README.md create mode 100644 macros/generic/advice/advice.edtx create mode 100644 macros/generic/advice/advice.ins (limited to 'macros/generic/advice') diff --git a/macros/generic/advice/INSTALL.md b/macros/generic/advice/INSTALL.md new file mode 100644 index 0000000000..96844efa8f --- /dev/null +++ b/macros/generic/advice/INSTALL.md @@ -0,0 +1,69 @@ +# Installation from the TDS archive + +If Advice is not (yet) offered by your TeX distribution, the easiest way to +install it is by downloading the TDS archive `advice.tds.zip` from [Advice's +CTAN page](https://ctan.org/pkg/advice), and unpacking it into your `texmf` +directory. You will most likely also have to do the same for an auxiliary +package Advice depends on: [CollArgs](https://ctan.org/pkg/collargs). + +Read on only if you have an unstoppable urge to install from source and/or +compile the manual or the documented source code. + +# Installation from the source + +## Getting the sources + +There are several options: + +* Download and unpack the zip archive of the package from [Advice's CTAN + page](https://ctan.org/pkg/advice). + +* Download and unpack the TDS archive, or copy the files from your local + distribution. The sources reside in `/source/generic/advice`. + +* Clone the [GitHub repository of + Memoize](https://github.com/sasozivanovic/memoize). + +## Generating the runtime files + +The easiest way to generate the runtime files is by running `make`. The +following command will generate runtime files for all supported formats +(currently: LaTeX, plain TeX and ConTeXt). + +``` +make advice.sty +``` + +Alternatively, you can generate these files manually. The source of this +package was written using [EasyDTX](https://ctan.org/pkg/easydtx). Therefore, +you first have to convert the `.edtx` file into a regular `.dtx`: + +``` +edtx2dtx advice.edtx > advice.dtx +``` + +The next step is standard. Produce the runtime files by compiling the +installation file: + +``` +tex advice.ins +``` + +Finally, if you require the ConTeXt runtime, replace all instances of +`\expanded` and `\unexpanded` in `t-advice.tex` by `\normalexpanded` and +`\normalunexpanded`, respectively. One way to do this is: + +``` +sed -i -s -e 's/\\\(un\)\?expanded/\\normal\1expanded/g;' t-advice.tex +``` + +## Installation + +It is recommended to install the files into a TDS-compliant `texmf` directory, +as usual. Inspect file `FILES` or the TDS archive `advice.tds.zip` to see what +goes where. + +# Compiling the documentation + +The documentation of this package is integrated into the documentation of +[Memoize](https://ctan.org/pkg/memoize), please continue there. diff --git a/macros/generic/advice/LICENCE b/macros/generic/advice/LICENCE new file mode 100644 index 0000000000..1b57559a83 --- /dev/null +++ b/macros/generic/advice/LICENCE @@ -0,0 +1,416 @@ +The LaTeX Project Public License +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + +LPPL Version 1.3c 2008-05-04 + +Copyright 1999 2002-2008 LaTeX3 Project + Everyone is allowed to distribute verbatim copies of this + license document, but modification of it is not allowed. + + +PREAMBLE +======== + +The LaTeX Project Public License (LPPL) is the primary license under +which the LaTeX kernel and the base LaTeX packages are distributed. + +You may use this license for any work of which you hold the copyright +and which you wish to distribute. This license may be particularly +suitable if your work is TeX-related (such as a LaTeX package), but +it is written in such a way that you can use it even if your work is +unrelated to TeX. + +The section `WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE', +below, gives instructions, examples, and recommendations for authors +who are considering distributing their works under this license. + +This license gives conditions under which a work may be distributed +and modified, as well as conditions under which modified versions of +that work may be distributed. + +We, the LaTeX3 Project, believe that the conditions below give you +the freedom to make and distribute modified versions of your work +that conform with whatever technical specifications you wish while +maintaining the availability, integrity, and reliability of +that work. If you do not see how to achieve your goal while +meeting these conditions, then read the document `cfgguide.tex' +and `modguide.tex' in the base LaTeX distribution for suggestions. + + +DEFINITIONS +=========== + +In this license document the following terms are used: + + `Work' + Any work being distributed under this License. + + `Derived Work' + Any work that under any applicable law is derived from the Work. + + `Modification' + Any procedure that produces a Derived Work under any applicable + law -- for example, the production of a file containing an + original file associated with the Work or a significant portion of + such a file, either verbatim or with modifications and/or + translated into another language. + + `Modify' + To apply any procedure that produces a Derived Work under any + applicable law. + + `Distribution' + Making copies of the Work available from one person to another, in + whole or in part. Distribution includes (but is not limited to) + making any electronic components of the Work accessible by + file transfer protocols such as FTP or HTTP or by shared file + systems such as Sun's Network File System (NFS). + + `Compiled Work' + A version of the Work that has been processed into a form where it + is directly usable on a computer system. This processing may + include using installation facilities provided by the Work, + transformations of the Work, copying of components of the Work, or + other activities. Note that modification of any installation + facilities provided by the Work constitutes modification of the Work. + + `Current Maintainer' + A person or persons nominated as such within the Work. If there is + no such explicit nomination then it is the `Copyright Holder' under + any applicable law. + + `Base Interpreter' + A program or process that is normally needed for running or + interpreting a part or the whole of the Work. + + A Base Interpreter may depend on external components but these + are not considered part of the Base Interpreter provided that each + external component clearly identifies itself whenever it is used + interactively. Unless explicitly specified when applying the + license to the Work, the only applicable Base Interpreter is a + `LaTeX-Format' or in the case of files belonging to the + `LaTeX-format' a program implementing the `TeX language'. + + + +CONDITIONS ON DISTRIBUTION AND MODIFICATION +=========================================== + +1. Activities other than distribution and/or modification of the Work +are not covered by this license; they are outside its scope. In +particular, the act of running the Work is not restricted and no +requirements are made concerning any offers of support for the Work. + +2. You may distribute a complete, unmodified copy of the Work as you +received it. Distribution of only part of the Work is considered +modification of the Work, and no right to distribute such a Derived +Work may be assumed under the terms of this clause. + +3. You may distribute a Compiled Work that has been generated from a +complete, unmodified copy of the Work as distributed under Clause 2 +above, as long as that Compiled Work is distributed in such a way that +the recipients may install the Compiled Work on their system exactly +as it would have been installed if they generated a Compiled Work +directly from the Work. + +4. If you are the Current Maintainer of the Work, you may, without +restriction, modify the Work, thus creating a Derived Work. You may +also distribute the Derived Work without restriction, including +Compiled Works generated from the Derived Work. Derived Works +distributed in this manner by the Current Maintainer are considered to +be updated versions of the Work. + +5. If you are not the Current Maintainer of the Work, you may modify +your copy of the Work, thus creating a Derived Work based on the Work, +and compile this Derived Work, thus creating a Compiled Work based on +the Derived Work. + +6. If you are not the Current Maintainer of the Work, you may +distribute a Derived Work provided the following conditions are met +for every component of the Work unless that component clearly states +in the copyright notice that it is exempt from that condition. Only +the Current Maintainer is allowed to add such statements of exemption +to a component of the Work. + + a. If a component of this Derived Work can be a direct replacement + for a component of the Work when that component is used with the + Base Interpreter, then, wherever this component of the Work + identifies itself to the user when used interactively with that + Base Interpreter, the replacement component of this Derived Work + clearly and unambiguously identifies itself as a modified version + of this component to the user when used interactively with that + Base Interpreter. + + b. Every component of the Derived Work contains prominent notices + detailing the nature of the changes to that component, or a + prominent reference to another file that is distributed as part + of the Derived Work and that contains a complete and accurate log + of the changes. + + c. No information in the Derived Work implies that any persons, + including (but not limited to) the authors of the original version + of the Work, provide any support, including (but not limited to) + the reporting and handling of errors, to recipients of the + Derived Work unless those persons have stated explicitly that + they do provide such support for the Derived Work. + + d. You distribute at least one of the following with the Derived Work: + + 1. A complete, unmodified copy of the Work; + if your distribution of a modified component is made by + offering access to copy the modified component from a + designated place, then offering equivalent access to copy + the Work from the same or some similar place meets this + condition, even though third parties are not compelled to + copy the Work along with the modified component; + + 2. Information that is sufficient to obtain a complete, + unmodified copy of the Work. + +7. If you are not the Current Maintainer of the Work, you may +distribute a Compiled Work generated from a Derived Work, as long as +the Derived Work is distributed to all recipients of the Compiled +Work, and as long as the conditions of Clause 6, above, are met with +regard to the Derived Work. + +8. The conditions above are not intended to prohibit, and hence do not +apply to, the modification, by any method, of any component so that it +becomes identical to an updated version of that component of the Work as +it is distributed by the Current Maintainer under Clause 4, above. + +9. Distribution of the Work or any Derived Work in an alternative +format, where the Work or that Derived Work (in whole or in part) is +then produced by applying some process to that format, does not relax or +nullify any sections of this license as they pertain to the results of +applying that process. + +10. a. A Derived Work may be distributed under a different license + provided that license itself honors the conditions listed in + Clause 6 above, in regard to the Work, though it does not have + to honor the rest of the conditions in this license. + + b. If a Derived Work is distributed under a different license, that + Derived Work must provide sufficient documentation as part of + itself to allow each recipient of that Derived Work to honor the + restrictions in Clause 6 above, concerning changes from the Work. + +11. This license places no restrictions on works that are unrelated to +the Work, nor does this license place any restrictions on aggregating +such works with the Work by any means. + +12. Nothing in this license is intended to, or may be used to, prevent +complete compliance by all parties with all applicable laws. + + +NO WARRANTY +=========== + +There is no warranty for the Work. Except when otherwise stated in +writing, the Copyright Holder provides the Work `as is', without +warranty of any kind, either expressed or implied, including, but not +limited to, the implied warranties of merchantability and fitness for a +particular purpose. The entire risk as to the quality and performance +of the Work is with you. Should the Work prove defective, you assume +the cost of all necessary servicing, repair, or correction. + +In no event unless required by applicable law or agreed to in writing +will The Copyright Holder, or any author named in the components of the +Work, or any other party who may distribute and/or modify the Work as +permitted above, be liable to you for damages, including any general, +special, incidental or consequential damages arising out of any use of +the Work or out of inability to use the Work (including, but not limited +to, loss of data, data being rendered inaccurate, or losses sustained by +anyone as a result of any failure of the Work to operate with any other +programs), even if the Copyright Holder or said author or said other +party has been advised of the possibility of such damages. + + +MAINTENANCE OF THE WORK +======================= + +The Work has the status `author-maintained' if the Copyright Holder +explicitly and prominently states near the primary copyright notice in +the Work that the Work can only be maintained by the Copyright Holder +or simply that it is `author-maintained'. + +The Work has the status `maintained' if there is a Current Maintainer +who has indicated in the Work that they are willing to receive error +reports for the Work (for example, by supplying a valid e-mail +address). It is not required for the Current Maintainer to acknowledge +or act upon these error reports. + +The Work changes from status `maintained' to `unmaintained' if there +is no Current Maintainer, or the person stated to be Current +Maintainer of the work cannot be reached through the indicated means +of communication for a period of six months, and there are no other +significant signs of active maintenance. + +You can become the Current Maintainer of the Work by agreement with +any existing Current Maintainer to take over this role. + +If the Work is unmaintained, you can become the Current Maintainer of +the Work through the following steps: + + 1. Make a reasonable attempt to trace the Current Maintainer (and + the Copyright Holder, if the two differ) through the means of + an Internet or similar search. + + 2. If this search is successful, then enquire whether the Work + is still maintained. + + a. If it is being maintained, then ask the Current Maintainer + to update their communication data within one month. + + b. If the search is unsuccessful or no action to resume active + maintenance is taken by the Current Maintainer, then announce + within the pertinent community your intention to take over + maintenance. (If the Work is a LaTeX work, this could be + done, for example, by posting to comp.text.tex.) + + 3a. If the Current Maintainer is reachable and agrees to pass + maintenance of the Work to you, then this takes effect + immediately upon announcement. + + b. If the Current Maintainer is not reachable and the Copyright + Holder agrees that maintenance of the Work be passed to you, + then this takes effect immediately upon announcement. + + 4. If you make an `intention announcement' as described in 2b. above + and after three months your intention is challenged neither by + the Current Maintainer nor by the Copyright Holder nor by other + people, then you may arrange for the Work to be changed so as + to name you as the (new) Current Maintainer. + + 5. If the previously unreachable Current Maintainer becomes + reachable once more within three months of a change completed + under the terms of 3b) or 4), then that Current Maintainer must + become or remain the Current Maintainer upon request provided + they then update their communication data within one month. + +A change in the Current Maintainer does not, of itself, alter the fact +that the Work is distributed under the LPPL license. + +If you become the Current Maintainer of the Work, you should +immediately provide, within the Work, a prominent and unambiguous +statement of your status as Current Maintainer. You should also +announce your new status to the same pertinent community as +in 2b) above. + + +WHETHER AND HOW TO DISTRIBUTE WORKS UNDER THIS LICENSE +====================================================== + +This section contains important instructions, examples, and +recommendations for authors who are considering distributing their +works under this license. These authors are addressed as `you' in +this section. + +Choosing This License or Another License +---------------------------------------- + +If for any part of your work you want or need to use *distribution* +conditions that differ significantly from those in this license, then +do not refer to this license anywhere in your work but, instead, +distribute your work under a different license. You may use the text +of this license as a model for your own license, but your license +should not refer to the LPPL or otherwise give the impression that +your work is distributed under the LPPL. + +The document `modguide.tex' in the base LaTeX distribution explains +the motivation behind the conditions of this license. It explains, +for example, why distributing LaTeX under the GNU General Public +License (GPL) was considered inappropriate. Even if your work is +unrelated to LaTeX, the discussion in `modguide.tex' may still be +relevant, and authors intending to distribute their works under any +license are encouraged to read it. + +A Recommendation on Modification Without Distribution +----------------------------------------------------- + +It is wise never to modify a component of the Work, even for your own +personal use, without also meeting the above conditions for +distributing the modified component. While you might intend that such +modifications will never be distributed, often this will happen by +accident -- you may forget that you have modified that component; or +it may not occur to you when allowing others to access the modified +version that you are thus distributing it and violating the conditions +of this license in ways that could have legal implications and, worse, +cause problems for the community. It is therefore usually in your +best interest to keep your copy of the Work identical with the public +one. Many works provide ways to control the behavior of that work +without altering any of its licensed components. + +How to Use This License +----------------------- + +To use this license, place in each of the components of your work both +an explicit copyright notice including your name and the year the work +was authored and/or last substantially modified. Include also a +statement that the distribution and/or modification of that +component is constrained by the conditions in this license. + +Here is an example of such a notice and statement: + + %% pig.dtx + %% Copyright 2008 M. Y. Name + % + % This work may be distributed and/or modified under the + % conditions of the LaTeX Project Public License, either version 1.3 + % of this license or (at your option) any later version. + % The latest version of this license is in + % https://www.latex-project.org/lppl.txt + % and version 1.3c or later is part of all distributions of LaTeX + % version 2008 or later. + % + % This work has the LPPL maintenance status `maintained'. + % + % The Current Maintainer of this work is M. Y. Name. + % + % This work consists of the files pig.dtx and pig.ins + % and the derived file pig.sty. + +Given such a notice and statement in a file, the conditions +given in this license document would apply, with the `Work' referring +to the three files `pig.dtx', `pig.ins', and `pig.sty' (the last being +generated from `pig.dtx' using `pig.ins'), the `Base Interpreter' +referring to any `LaTeX-Format', and both `Copyright Holder' and +`Current Maintainer' referring to the person `M. Y. Name'. + +If you do not want the Maintenance section of LPPL to apply to your +Work, change `maintained' above into `author-maintained'. +However, we recommend that you use `maintained', as the Maintenance +section was added in order to ensure that your Work remains useful to +the community even when you can no longer maintain and support it +yourself. + +Derived Works That Are Not Replacements +--------------------------------------- + +Several clauses of the LPPL specify means to provide reliability and +stability for the user community. They therefore concern themselves +with the case that a Derived Work is intended to be used as a +(compatible or incompatible) replacement of the original Work. If +this is not the case (e.g., if a few lines of code are reused for a +completely different task), then clauses 6b and 6d shall not apply. + + +Important Recommendations +------------------------- + + Defining What Constitutes the Work + + The LPPL requires that distributions of the Work contain all the + files of the Work. It is therefore important that you provide a + way for the licensee to determine which files constitute the Work. + This could, for example, be achieved by explicitly listing all the + files of the Work near the copyright notice of each file or by + using a line such as: + + % This work consists of all files listed in manifest.txt. + + in that place. In the absence of an unequivocal list it might be + impossible for the licensee to determine what is considered by you + to comprise the Work and, in such a case, the licensee would be + entitled to make reasonable conjectures as to which files comprise + the Work. + diff --git a/macros/generic/advice/Makefile b/macros/generic/advice/Makefile new file mode 100644 index 0000000000..421bcdbfc9 --- /dev/null +++ b/macros/generic/advice/Makefile @@ -0,0 +1,8 @@ +.PRECIOUS: %.dtx + +%.dtx: %.edtx + edtx2dtx $< > $@ + +%.sty %.tex t-%.tex &: %.ins %.dtx + tex $< + sed -i -s -e 's/\\\(un\)\?expanded/\\normal\1expanded/g;' t-$*.tex diff --git a/macros/generic/advice/README.md b/macros/generic/advice/README.md new file mode 100644 index 0000000000..0b001d9df8 --- /dev/null +++ b/macros/generic/advice/README.md @@ -0,0 +1,8 @@ +Like its namesake from the Emacs world, package Advice implements a generic +framework for extending the functionality of selected commands and +environments. + +This package was developed as an auxiliary package of +[Memoize](https://ctan.org/pkg/memoize). This is why it is, somewhat +unconventionally, documented alongside that package. This applies to both the +manual and the documented code listing. diff --git a/macros/generic/advice/advice.edtx b/macros/generic/advice/advice.edtx new file mode 100644 index 0000000000..ee9486226e --- /dev/null +++ b/macros/generic/advice/advice.edtx @@ -0,0 +1,1095 @@ +% \iffalse +% +% advice.edtx (this is not a .dtx file; to produce a .dtx, process it with edtx2dtx) +% +%% This file is a part of Advice, a TeX package implementing a generic +%% framework for extending the functionality of selected commands and +%% environments, available at https://ctan.org/pkg/advice and +%% https://github.com/sasozivanovic/advice. +%% +%% Copyright (c) 2023- Saso Zivanovic +%% (Sa\v{s}o \v{Z}ivanovi\'{c}) +%% +%% This work may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License, either version 1.3c of this license or (at +%% your option) any later version. The latest version of this license is in +%% https://www.latex-project.org/lppl.txt and version 1.3c or later is part of +%% all distributions of LaTeX version 2008 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% The Current Maintainer of this work is Saso Zivanovic. +%% +%% The files belonging to this work and covered by LPPL are listed in `FILES`. +% +% \fi +% +% \begin{macrocode} +% +% \relax +% +%<*main> +%\ProvidesPackage{advice}[2023/10/10 v1.0.0 Extend commands and environments] +%%D \module[ +%%D file=t-advice.tex, +%%D version=1.0.0, +%%D title=Advice, +%%D subtitle=Extend commands and environments, +%%D author=Saso Zivanovic, +%%D date=2023-10-10, +%%D copyright=Saso Zivanovic, +%%D license=LPPL, +%%D ] +%\writestatus{loading}{ConTeXt User Module / advice} +%\unprotect +%\startmodule[advice] +% \paragraph{Required packages} +%\RequirePackage{collargs} +%\input collargs +%\input t-collargs +% +% \subsubsection{Installation into a keypath} +% +% \begin{handlerskey}{.install advice} +% This handler installs the advising mechanism into the handled path, which +% we shall henceforth also call the (advice) namespace. +\pgfkeys{ + /handlers/.install advice/.code={% + \edef\auto@install@namespace{\pgfkeyscurrentpath}% + \def\advice@install@setupkey{advice}% + \def\advice@install@activation{immediate}% + \pgfqkeys{/advice/install}{#1}% + \expanded{\noexpand\advice@install + {\auto@install@namespace}% + {\advice@install@setupkey}% + {\advice@install@activation}% + }% + }, +% \end{handlerskey} +% +% \begin{adviceinstallkey}{setup key,activation} +% These keys can be used in the argument of |.install advice| to configure +% the installation. By default, the setup key is |advice| and |activation| is +% |immediate|. + /advice/install/.cd, + setup key/.store in=\advice@install@setupkey, + activation/.is choice, + activation/.append code=\def\advice@install@activation{#1}, + activation/immediate/.code={}, + activation/deferred/.code={}, +} +% \end{adviceinstallkey} +% +% |#1| is the installation keypath (in Memoize, |/mmz|); |#2| is the setup key +% name (in Memoize, |auto|, and this is why we document it as such); |#3| is +% the initial activation regime. +\def\advice@install#1#2#3{% + % Switch to the installation keypath. + \pgfqkeys{#1}{% + % \begin{key}{auto, auto csname, auto key, auto', auto csname', auto key'} + % These keys submit a command or environment to advising. The + % namespace is hard-coded into these keys via |#1|; their arguments are + % the command/environment (cs)name, and setup keys belonging to path + % \meta{installation keypath}|/\meta{setup key name}|. + #2/.code 2 args={% + % Call the internal setup macro, wrapping the received keylist into a + % |pgfkeys| invocation. + \AdviceSetup{#1}{#2}{##1}{\pgfqkeys{#1/#2}{##2}}% + % Activate if not already activated (this can happen when updating the + % configuration). Note we don't call |\advice@activate| directly, but use + % the public keys; in this way, activation is automatically deferred if + % so requested. (We don't use |\pgfkeysalso| to allow |auto| being called + % from any path.) + \pgfqkeys{#1}{try activate, activate={##1}}% + }, + % A variant without activation. + #2'/.code 2 args={% + \AdviceSetup{#1}{#2}{##1}{\pgfqkeys{#1/#2}{##2}}% + }, + #2 csname/.style 2 args={ + #2/.expand once=\expandafter{\csname ##1\endcsname}{##2}, + }, + #2 csname'/.style 2 args={ + #2'/.expand once=\expandafter{\csname ##1\endcsname}{##2}, + }, + #2 key/.style 2 args={ + #2/.expand once=% + \expandafter{\csname pgfk@##1/.@cmd\endcsname}% + {collector=\advice@pgfkeys@collector,##2}, + }, + #2 key'/.style 2 args={ + #2'/.expand once=% + \expandafter{\csname pgfk@##1/.@cmd\endcsname}% + {collector=\advice@pgfkeys@collector,##2}, + }, + % \end{key} + % + % \begin{key}{activation} + % This key, residing in the installation keypath, forwards the request to + % the |/advice| path |activation| subkeys, which define |activate| and + % friends in the installation keypath. Initially, the activation regime + % is whatever the user has requested using the |.install advice| argument + % (here |#3|). + activation/.style={/advice/activation/##1={#1}}, + activation=#3, + % \end{key} + % + % \begin{key}{activate deferred} + % The deferred activations are collected in this style, see + % section~ref{sec:code:advice:activation} for details. + activate deferred/.code={}, + % \end{key} + % + % \begin{key}{activate csname, deactivate csname} + % For simplicity of implementation, the |csname| versions of |activate| + % and |deactivate| accept a single \meta{csname}. This way, they can be + % defined right away, as they don't change with the type of activation + % (immediate vs.\ deferred). + activate csname/.style={activate/.expand once={\csname##1\endcsname}}, + deactivate csname/.style={activate/.expand once={\csname##1\endcsname}}, + % \end{key} + % + % \begin{key}{activate key, deactivate key} + % (De)activation of |pgfkeys| keys. Accepts a list of key names, requires + % full key names. + activate key/.style={activate@key={#1/activate}{##1}}, + deactivate key/.style={activate@key={#1/deactivate}{##1}}, + activate@key/.code n args=2{% + \def\advice@temp{}% + \def\advice@do####1{% + \eappto\advice@temp{,\expandonce{\csname pgfk@####1/.@cmd\endcsname}}}% + \forcsvlist\advice@do{##2}% + \pgfkeysalso{##1/.expand once=\advice@temp}% + }, + % \end{key} + % + % The rest of the keys defined below reside in the |auto| subfolder of the + % installation keypath. + #2/.cd, + % + % \begin{mmzautokey}{run conditions, outer handler, bailout handler, collector, + % args, collector options, clear collector options, raw collector options, + % clear raw collector options, inner handler, options, clear options} + % These keys are used to setup the handling of the command or + % environment. The storage macros (|\AdviceRunConditions| etc.) have + % public names as they also play a crucial role in the handler + % definitions, see section~\ref{sec:code:advice:handle}. \indentmacrocode + run conditions/.store in=\AdviceRunConditions, + bailout handler/.store in=\AdviceBailoutHandler, + outer handler/.store in=\AdviceOuterHandler, + collector/.store in=\AdviceCollector, + collector options/.code={\appto\AdviceCollectorOptions{,##1}}, + clear collector options/.code={\def\AdviceCollectorOptions{}}, + raw collector options/.code={\appto\AdviceRawCollectorOptions{##1}}, + clear raw collector options/.code={\def\AdviceRawCollectorOptions{}}, + args/.store in=\AdviceArgs, + inner handler/.store in=\AdviceInnerHandler, + options/.code={\appto\AdviceOptions{,##1}}, + clear options/.code={\def\AdviceOptions{}}, + % \noindentmacrocode + % A user-friendly way to set |options|: any unknown key is an option. + .unknown/.code={% + \eappto{\AdviceOptions}{,\pgfkeyscurrentname={\unexpanded{##1}}}% + }, + % The default values of the keys, which equal the initial values for + % commands, as assigned by |\advice@setup@init@command|. + run conditions/.default=\AdviceRuntrue, + bailout handler/.default=\relax, + outer handler/.default=\advice@default@outer@handler, + collector/.default=\advice@CollectArgumentsRaw, + collector options/.value required, + raw collector options/.value required, + args/.default=\advice@noargs, + inner handler/.default=\advice@error@noinnerhandler, + options/.value required, + % \end{mmzautokey} + % + % \begin{mmzautokey}{reset} + % This key resets the advice settings to their initial values, which + % depend on whether we're handling a command or environment. + reset/.code={\csname\advice@setup@init@\AdviceType\endcsname}, + % \end{mmzautokey} + % + % \begin{mmzautokey} + % The code given here will be executed once we exit the setup group. + % |integrated driver| of Memoize uses it to declare a conditional. + after setup/.code={\appto\AdviceAfterSetup{##1}}, + % \end{mmzautokey} + % + % In \hologo{LaTeX}, we finish the installation by submitting |\begin|; the + % submission is funky, because the run conditions handler actually hacks + % the standard handling procedure. Note that if |\begin| is not + % activated, environments will not be handled, and that the automatic + % activation might be deffered. + %#1/#2=\begin{run conditions=\advice@begin@rc}, + }% +} +% +% +% \subsubsection{Submitting a command or environment} +% \label{sec:code:advice:setup} +% +% \begin{macro}{\AdviceSetup,\AdviceName,\AdviceType} +% Macro |\advice@setup| is called by key |auto| to submit a command or +% environment to advising. It receives four arguments: |#1| is the +% installation keypath / storage namespace: |#2| is the name of the setup +% key; |#3| is the submitted command or environment; |#4| is the setup code +% (which is only grabbed by |\advice@setup@i|). +% +% Executing this macro defines macros |\AdviceName|, holding the control +% sequence of the submitted command or the environment name, and |\AdviceType|, +% holding |command| or |environment|; they are used to set up some initial +% values, and may be used by user-defined keys in the |auto| path, as well +% (see |/mmz/auto/noop| for an example). The macro then performs internal +% initialization, and finally calls the second part, |\advice@setup@i|, with +% the command's \emph{storage} name as the first argument. +% +% This macro also serves as the programmer's interface to |auto|, the idea +% being that an advanced user may write code |#4| which defined the settings +% macros (|\AdviceOuterHandler| etc.) without deploying |pgfkeys|. (Also note +% that activation at the end only occurs through the |auto| interface.) +\def\AdviceSetup#1#2#3{% + % Open a group, so that we allow for embedded |auto| invocations. + \begingroup + \def\AdviceName{#3}% + % Command, complain, or environment? + \collargs@cs@cases{#3}{% + \def\AdviceType{command}% + \advice@setup@init@command + \advice@setup@i{#3}{#1}{#3}% + }{% + \advice@error@advice@notcs{#1/#2}{#3}% + }{% + \def\AdviceType{environment}% + \advice@setup@init@environment + %\advice@setup@i{#3}% + %\expandafter\advice@setup@i\expandafter{\csname #3\endcsname}% + %\expandafter\advice@setup@i\expandafter{\csname start#3\endcsname}% + {#1}{#3}% + }% +} +% The arguments of |\advice@setup@i| are a bit different than for +% |\advice@setup|, because we have inserted the storage name as |#1| above, and +% we lost the setup key name |#2|. Here, |#2| is the installation keypath / +% storage namespace, |#3| is the submitted command or environment; and |#4| is +% the setup code. +% +% What is the difference between the storage name (|#1|) and the command\slash +% environment name (|#3|, and also the contents of |\AdviceName|), and why do we +% need both? For commands, there is actually no difference; for example, when +% submitting command |\foo|, we end up with |#1|=|#3|=|\foo|. And there is +% also no difference for \hologo{LaTeX} environments; when submitting +% environment |foo|, we get |#1|=|#3|=|foo|. But in \hologo{plainTeX}, +% |#1|=|\foo| and |#3|=|foo|, and in \hologo{ConTeXt}, |#1|=|\startfoo| and +% |#3|=|foo| --- which should explain the guards and |\expandafter|s above. +% +% And why both |#1| and |#3|? When a handled command is executed, it loads its +% configuration from a macro determined by the storage namespace and the +% (|\string|ified) storage name, e.g.\ |/mmz| and |\foo|. In \hologo{plainTeX} +% and \hologo{ConTeXt}, each environment is started by a dedicated command, +% |\foo| or |\startfoo|, so these control sequences (|\string|ified) must act +% as storage names. (Not so in \hologo{LaTeX}, where an environment +% configuration is loaded by |\begin|'s handler, which can easily work with +% storage name |foo|. Even more, having |\foo| as an environment storage +% name would conflict with the storage name for the (environment-internal) +% command |\foo| --- yes, we can submit either |foo| or |\foo|, or both, to +% advising.) +\def\advice@setup@i#1#2#3#4{% + % Load the current configuration of the handled command or environment --- if + % it exists. + \advice@setup@init@i{#2}{#1}% + \advice@setup@init@I{#2}{#1}% + \def\AdviceAfterSetup{}% + % Apply the setup code/keys. + #4% + % Save the resulting configuration. This closes the group, because the config + % is saved outside it. + \advice@setup@save{#2}{#1}% +} +% \end{macro} +% +% \paragraph{Initialize} the configuration of a command or environment. Note +% that the default values of the keys equal the initial values for commands. +% Nothing would go wrong if these were not the same, but it's nice that the +% end-user can easily revert to the initial values. +\def\advice@setup@init@common{% + \def\AdviceRunConditions{\AdviceRuntrue}% + \def\AdviceBailoutHandler{\relax}% + \def\AdviceOuterHandler{\advice@default@outer@handler}% + \def\AdviceCollector{\advice@CollectArgumentsRaw}% + \def\AdviceCollectorOptions{}% + \def\AdviceInnerHandler{\advice@error@noinnerhandler}% + \def\AdviceOptions{}% +} +\def\advice@setup@init@command{% + \advice@setup@init@common + \def\AdviceRawCollectorOptions{}% + \def\AdviceArgs{\advice@noargs}% +} +\def\advice@setup@init@environment{% + \advice@setup@init@common + \edef\AdviceRawCollectorOptions{% + \noexpand\collargsEnvironment{\AdviceName}% + % When grabbing an environment body, the end-tag will be included. This + % makes it possible to have the same inner handler for commands and + % environments. + \noexpand\collargsEndTagtrue + }% + \def\AdviceArgs{+b}% +} +% We need to initialize |\AdviceOuterHandler| etc.\ so that |\advice@setup@store| +% will work. +\advice@setup@init@command +% +% \paragraph.{The configuration storage} +% +% The remaining macros in this subsection deal with the configuration storage +% space, which is set up in a way to facilitate fast loading during the +% execution of handled commands and environments. +% +% The configuration of a command or environment is stored in two parts: the +% first stage settings comprise the run conditions, the bailout handler and the +% outer handler; the second stage settings contain the rest. When a handled +% command is invoked, only the first stage settings are immediately loaded, for +% speed; the second stage settings are only loaded if the run conditions are +% satisfied. +% +% \begin{macro}{\advice@init@i,\advice@init@I} +% The two-stage settings are stored in control sequences +% |\advice@i|\meta{namespace}|//|\meta{storage name} and +% |\advice@I|\meta{namespace}|//|\meta{storage name}, respectively, and +% accessed using macros |\advice@init@i| and |\advice@init@I|. +% +% Each setting storage macro contains a sequence of items, where each item is +% either of form |\def\AdviceSetting|\marg{value}. This allows us store +% multiple settings in a single macro (rather than define each +% control-sequence-valued setting separately, which would use more string +% memory), and also has the consequence that we don't require the handlers to +% be defined when submitting a command (whether that's good or bad could be +% debated: as things stand, any typos in handler declarations will only yield +% an error once the handled command is executed). +\def\advice@init@i#1#2{\csname advice@i#1//\string#2\endcsname} +\def\advice@init@I#1#2{\csname advice@I#1//\string#2\endcsname} +% We make a copy of these for setup; the originals might be swapped for +% tracing purposes. +\let\advice@setup@init@i\advice@init@i +\let\advice@setup@init@I\advice@init@I +% \end{macro} +% +% \begin{macro}{\advice@setup@save} +% To save the configuration at the end of the setup, we construct the storage +% macros out of |\AdviceRunConditions| and friends. Stage-one contains only +% |\AdviceRunConditions| and |\AdviceBailoutHandler|, so that +% |\advice@handle| can bail out as quickly as possible if the run conditions +% are not met. +\def\advice@setup@save#1#2{% + \expanded{% + % Close the group before saving. Note that |\expanded| has already expanded + % the settings macros. + \endgroup + \noexpand\csdef{advice@i#1//\string#2}{% + \def\noexpand\AdviceRunConditions{\expandonce\AdviceRunConditions}% + \def\noexpand\AdviceBailoutHandler{\expandonce\AdviceBailoutHandler}% + }% + \noexpand\csdef{advice@I#1//\string#2}{% + \def\noexpand\AdviceOuterHandler{\expandonce\AdviceOuterHandler}% + \def\noexpand\AdviceCollector{\expandonce\AdviceCollector}% + \def\noexpand\AdviceRawCollectorOptions{\expandonce\AdviceRawCollectorOptions}% + \def\noexpand\AdviceCollectorOptions{\expandonce\AdviceCollectorOptions}% + \def\noexpand\AdviceArgs{\expandonce\AdviceArgs}% + \def\noexpand\AdviceInnerHandler{\expandonce\AdviceInnerHandler}% + \def\noexpand\AdviceOptions{\expandonce\AdviceOptions}% + }% + \expandonce{\AdviceAfterSetup}% + }% +} +% \end{macro} +% \label{sec:code:advice:activation} +% +% \begin{advicekey}{activation/immediate, activation/deferred} +% The two subkeys of |/advice/activation| install the immediate and the +% deferred activation code into the installation keypath. They are invoked +% by \meta{installation keypath}|/activation=|\meta{type}. +% +% Under the deferred activation regime, the commands are not (de)activated +% right away. Rather, the (de)activation calls are collected in style +% |activate deferred|, which should be executed by the installation keypath +% owner, if and when they so desire. (Be sure to switch to +% |activation=immediate| before executing |activate deferred|, otherwise the +% activation will only be deferred once again.) +\pgfkeys{ + /advice/activation/deferred/.style={ + #1/activate/.style={% + activate deferred/.append style={#1/activate={##1}}}, + #1/deactivate/.style={% + activate deferred/.append style={#1/deactivate={##1}}}, + #1/force activate/.style={% + activate deferred/.append style={#1/force activate={##1}}}, + #1/try activate/.style={% + activate deferred/.append style={#1/try activate={##1}}}, + }, +% \end{advicekey} +% +% \begin{key}{activate, deactivate, force activate, try activate} +% The ``real,'' immediate |activate| and |deactivate| take a comma-separated +% list of commands or environments and (de)activate them. If |try activate| +% is in effect, no error is thrown upon failure. If |force activate| is in +% effect, activation proceeds even if we already had the original definition; +% it does not apply to deactivation. These conditionals are set to false +% after every invocation of key |(de)activate|, so that they only apply to +% the immediately following |(de)activate|. (|#1| below is the +% \meta{namespace}; |##1| is the list of commands to be (de)activated.) + /advice/activation/immediate/.style={ + #1/activate/.code={% + \forcsvlist{\advice@activate{#1}}{##1}% + \advice@activate@forcefalse + \advice@activate@tryfalse + }, + #1/deactivate/.code={% + \forcsvlist{\advice@deactivate{#1}}{##1}% + \advice@activate@forcefalse + \advice@activate@tryfalse + }, + #1/force activate/.is if=advice@activate@force, + #1/try activate/.is if=advice@activate@try, + }, +} +\newif\ifadvice@activate@force +\newif\ifadvice@activate@try +% \end{key} +% +% \begin{macro}{\advice@original@csname,\advice@original@cs,\AdviceGetOriginal} +% Activation replaces the original meaning of the handled command with our +% definition. We store the original definition into control sequence +% |\advice@o|\meta{namespace}|//|\meta{storage name} (with a |\string|ified +% \meta{storage name}). Internally, during (de)activation and handling, we +% access it using |\advice@original@csname| and |\advice@original@cs|. Publicly +% it should always be accessed by |\AdviceGetOriginal|, which returns the +% argument control sequence if that control sequence is not handled. +% +% Using the internal command outside the handling context, we could fall +% victim to scenario such as the following. When we memoize something +% containing a |\label|, the produced cc-memo contains code eventually +% executing the original |\label|. If we called the original |\label| via +% the internal macro there, and the user |deactivate|d |\label| on a +% subsequent compilation, the cc-memo would not call |\label| anymore, but +% |\relax|, resulting in a silent error. Using |\AdviceGetOriginal|, the +% original |\label| will be executed even when not activated. +% +% However, not all is bright with |\AdviceGetOriginal|. Given an activated +% control sequence (|#2|), a typo in the namespace argument (|#1|) will lead +% to an infinite loop upon the execution of |\AdviceGetOriginal|. In the +% manual, we recommend defining a namespace-specific macro to avoid such +% typos. +\def\advice@original@csname#1#2{advice@o#1//\string#2} +\def\advice@original@cs#1#2{\csname advice@o#1//\string#2\endcsname} +\def\AdviceGetOriginal#1#2{% + \ifcsname advice@o#1//\string#2\endcsname + \csname advice@o#1//\string#2\expandafter\endcsname + \else + \expandafter#2% + \fi +} +% \end{macro} +% +% \begin{macro}{\advice@activate,\advice@deactivate} +% These macros execute either the command, or the environment (de)activator. +\def\advice@activate#1#2{% + \collargs@cs@cases{#2}% + {\advice@activate@cmd{#1}{#2}}% + {\advice@error@activate@notcsorenv{}{#1}}% + {\advice@activate@env{#1}{#2}}% +} +\def\advice@deactivate#1#2{% + \collargs@cs@cases{#2}% + {\advice@deactivate@cmd{#1}{#2}}% + {\advice@error@activate@notcsorenv{de}{#1}}% + {\advice@deactivate@env{#1}{#2}}% +} +% \end{macro} +% +% \begin{macro}{\advice@activate@cmd} +% We are very careful when we're activating a command, because activating +% means rewriting its original definition. Configuration by |auto| did not +% touch the original command; activation will. So, the leitmotif of this +% macro: safety first. (|#1| is the namespace, and |#2| is the command to be +% activated.) +\def\advice@activate@cmd#1#2{% + % Is the command defined? + \ifdef{#2}{% + % Yes, the command is defined. Let's see if it's safe to activate it. + % We'll do this by checking whether we have its original definition in our + % storage. If we do, this means that we have already activated the + % command. Activating it twice would lead to the loss of the original + % definition (because the second activation would store our own + % redefinition as the original definition) and consequently an infinite + % loop (because once --- well, if --- the handler tries to invoke the + % original command, it will execute itself all over). + \ifcsdef{\advice@original@csname{#1}{#2}}{% + % Yes, we have the original definition, so the safety check failed, and + % we shouldn't activate again. Unless \dots\ how does its current + % definition look like? + \advice@if@our@definition{#1}{#2}{% + % Well, the current definition of the command matches what we would put + % there ourselves. The command is definitely activated, and we refuse + % to activate again, as that would destroy the original definition. + \advice@activate@error@activated{#1}{#2}{Command}{already}% + }{% + % We don't recognize the current definition as our own code (despite + % the fact that we have surely activated the commmand before, given the + % result of the first safety check). It appears that someone else was + % playing fast and loose with the same command, and redefined it after + % our activation. (In fact, if that someone else was another instance + % of Advice, from another namespace, forcing the activation will result + % in the loss of the original definition and the infinite loop.) So it + % \emph{should} be safe to activate it (again) \dots\ but we won't do + % it unless the user specifically requested this using |force + % activate|. Note that without |force activate|, we would be stuck in + % this branch, as we could neither activate (again) nor deactivate the + % command. + \ifadvice@activate@force + \advice@activate@cmd@do{#1}{#2}% + \else + \advice@activate@error@activated{#1}{#2}{Command}{already}% + \fi + }% + }{% + % No, we don't have the command's original definition, so it was not yet + % activated, and we may activate it. + \advice@activate@cmd@do{#1}{#2}% + }% + }{% + \advice@activate@error@undefined{#1}{#2}{Command}{}% + }% +} +% \end{macro} +% +% \begin{macro}{\advice@deactivate@cmd} +% The deactivation of a command follows the same template as activation, but +% with a different logic, and of course a different effect. In order to +% deactivate a command, both safety checks discussed above must be satisfied: +% we must have the command's original definition, \emph{and} our redefinition +% must still reside in the command's control sequence --- the latter +% condition prevents overwriting someone else's redefinition with the +% original command. As both conditions must be unavoidably fulfilled, |force +% activate| has no effect in deactivation (but |try activate| has). +\def\advice@deactivate@cmd#1#2{% + \ifdef{#2}{% + \ifcsdef{\advice@original@csname{#1}{#2}}{% + \advice@if@our@definition{#1}{#2}{% + \advice@deactivate@cmd@do{#1}{#2}% + }{% + \advice@deactivate@error@changed{#1}{#2}% + }% + }{% + \advice@activate@error@activated{#1}{#2}{Command}{not yet}% + }% + }{% + \advice@activate@error@undefined{#1}{#2}{Command}{de}% + }% +} +% \end{macro} +% +% \begin{macro}{\advice@if@our@definition} +% This macro checks whether control sequence |#2| was already activated (in +% namespace |#1|) in the sense that its current definition contains the code +% our activation would put there: |\advice@handle{#1}{#2}| (protected). +\def\advice@if@our@definition#1#2{% + \protected\def\advice@temp{\advice@handle{#1}{#2}}% + \ifx#2\advice@temp + \expandafter\@firstoftwo + \else + \expandafter\@secondoftwo + \fi +} +% \end{macro} +% +% \begin{macro}{\advice@activate@cmd@do} +% This macro saves the original command, and redefines its control sequence. +% Our redefinition must be |\protected| --- even if the original command +% wasn't fragile, our replacement certainly is. (Note that as we require +% \hologo{eTeX} anyway, we don't have to pay attention to \hologo{LaTeX}'s +% robust commands by redefining their ``inner'' command. Protecting our +% replacement suffices.) +\def\advice@activate@cmd@do#1#2{% + \cslet{\advice@original@csname{#1}{#2}}#2% + \protected\def#2{\advice@handle{#1}{#2}}% + \PackageInfo{advice (#1)}{Activated command "\string#2"}% +} +% \end{macro} +% +% \begin{macro}{\advice@deactivate@cmd@do} +% This macro restores the original command, and removes its definition from +% our storage --- this also serves as a signal that the command is not +% activated anymore. +\def\advice@deactivate@cmd@do#1#2{% + \letcs#2{\advice@original@csname{#1}{#2}}% + \csundef{\advice@original@csname{#1}{#2}}% + \PackageInfo{advice (#1)}{Deactivated command "\string#2"}% +} +% \end{macro} +% +% +% \subsubsection{Executing a handled command} +% \label{sec:code:advice:handle} +% +% \begin{macro}{\advice@handle} +% An invocation of this macro is what replaces the original command and runs +% the whole shebang. The system is designed to bail out as quickly as +% necessary if the run conditions are not met (plus \hologo{LaTeX}'s |\begin| +% will receive a very special treatment for this reason). +% +% We first check the run conditions, and bail out if they are not satisfied. +% Note that only the stage-one config is loaded at this point. It sets up +% the following macros (while they are public, neither the end user not the +% installation keypath owner should ever have to use them): +% +% \begin{itemize} +% \item \PrintMainMacro{\AdviceRunConditions} executes |\AdviceRuntrue| if the +% command should be handled; set by |run conditions|. +% \item \PrintMainMacro{\AdviceBailoutHandler} will be executed if the +% command will not be handled, after all; set by |bailout handler|. +% \end{itemize} +\def\advice@handle#1#2{% + \advice@init@i{#1}{#2}% + \AdviceRunfalse + \AdviceRunConditions + \advice@handle@rc{#1}{#2}% +} +% \end{macro} +% +% \begin{macro}{\advice@handle@rc} +% We continue the handling in a new macro, because this is the point where +% the handler for |\begin| will hack into the regular flow of events. +\def\advice@handle@rc#1#2{% + \ifAdviceRun + \expandafter\advice@handle@outer + \else + % Bailout is simple: we first execute the handler, and then the original command. + \AdviceBailoutHandler + \expandafter\advice@original@cs + \fi + {#1}{#2}% +} +% \end{macro} +% +% \begin{macro}{\advice@handle@outer} +% To actually handle the command, we first setup some macros: +% +% \begin{itemize} +% \item \PrintMainMacro{\AdviceNamespace} holds the installation keypath / +% storage name space. +% \item \PrintMainMacro{\AdviceName} holds the control sequence of the handled +% command, or the environment name. +% \item \PrintMainMacro{\AdviceReplaced} holds the ``substituted'' code. For +% commands, this is the same as |\AdviceName|. For environment |foo|, it +% equals |\begin{foo}| in \hologo{LaTeX}, |\foo| in \hologo{plainTeX} and +% |\startfoo| in \hologo{ConTeXt}. +% \item \PrintMainMacro{\AdviceOriginal} executes the original definition of +% the handled command or environment. +% \end{itemize} +\def\advice@handle@outer#1#2{% + \def\AdviceNamespace{#1}% + \def\AdviceName{#2}% + \let\AdviceReplaced\AdviceName + \def\AdviceOriginal{\AdviceGetOriginal{#1}{#2}}% + % We then load the stage-two settings. This defines the following macros: + % \begin{itemize} + % \item \PrintMainMacro{\AdviceOuterHandler} will effectively replace the + % command, if it will be handled; set by |outer handler|. + % \item \PrintMainMacro{\AdviceCollector} collects the arguments of the handled + % command, perhaps consulting |\AdviceArgs| to learn about its argument + % structure. + % \item \PrintMainMacro{\AdviceRawCollectorOptions} contains the options which + % will be passed to the argument collector, in the ``raw'' format. + % \item \PrintMainMacro{\AdviceCollectorOptions} contains the additional, + % user-specified options which will be passed to the argument collector. + % \item \PrintMainMacro{\AdviceArgs} contains the |xparse|-style argument + % specification of the command, or equals |\advice@noargs| to signal that + % command was defined using |xparse| and that the argument specification + % should be retrieved automatically. + % \item \PrintMainMacro{\AdviceInnerHandler} is called by the argument + % collector once it finishes its work. It receives all the collected + % arguments as a single (braced) argument. + % \item \PrintMainMacro{\AdviceOptions} holds options which may be used by the + % outer or the inner handler; Advice does not need or touch + % them. + % \end{itemize} + \advice@init@I{#1}{#2}% + % All prepared, we execute the outer handler. + \AdviceOuterHandler +} +% \end{macro} +% +% \begin{macro}{\ifAdviceRun} +% This conditional is set by the run conditions macro to signal whether we +% should run the outer (true) or the bailout (false) handler. +\newif\ifAdviceRun +% \end{macro} +% +% \begin{macro}{\advice@default@outer@handler} +% The default outer handler merely executes the argument collector. Note +% that it works for both commands and environments. +\def\advice@default@outer@handler{% + \AdviceCollector +} +% \end{macro} +% +% \begin{macro}{\advice@CollectArgumentsRaw} +% This is the default collector, which will collect the argument using +% CollArgs' command |\CollectArgumentsRaw|. It will provide that command +% with: +% \begin{itemize} +% \item the collector options, given in the raw format: the caller +% (|\collargsCaller|), the raw options (|\AdviceRawCollectorOptions|) and the +% user options (|\AdviceRawCollectorOptions|, wrapped in |\collargsSet|; +% \item the argument specification |\AdviceArgs| of the handled command; and +% \item the inner handler |\AdviceInnerHandler| to execute after collecting the +% arguments; the inner handler receives the collected arguments as a single +% braced argument. +% \end{itemize} +% If the argument specification is not defined (either the user did not set +% it, or has reset it by writing |args| without a value), it is assumed that +% the handled command was defined by |xparse| and |\AdviceArgs| will be +% retrieved by |\GetDocumentCommandArgSpec|. +% \begin{listingregion}{_advice-CollectArgumentsRaw.tex} +\def\advice@CollectArgumentsRaw{% + \AdviceIfArgs{}{% + \expandafter\GetDocumentCommandArgSpec\expandafter{\AdviceName}% + \let\AdviceArgs\ArgumentSpecification + }% + \expanded{% + \noexpand\CollectArgumentsRaw{% + \noexpand\collargsCaller{\expandonce\AdviceName}% + \expandonce\AdviceRawCollectorOptions + \ifdefempty\AdviceCollectorOptions{}{% + \noexpand\collargsSet{\expandonce\AdviceCollectorOptions}% + }% + }% + {\expandonce\AdviceArgs}% + {\expandonce\AdviceInnerHandler}% + }% +} +% \end{listingregion} +% \end{macro} +% +% \begin{macro}{\AdviceIfArgs} +% If the value of |args| is ``real'', i.e.\ an |xparse| argument +% specification, execute the first argument. If |args| was set to the +% special value |\advice@noargs|, signaling a command defined by +% |\NewDocumentCommand| or friends, execute the second argument. (Ok, in +% reality anything other than |\advice@noargs| counts as real ``real''.) +\def\advice@noargs@text{\advice@noargs} +\def\AdviceIfArgs{% + \ifx\AdviceArgs\advice@noargs@text + \expandafter\@secondoftwo + \else + \expandafter\@firstoftwo + \fi +} +% \end{macro} +% +% \begin{macro}{\advice@pgfkeys@collector} +% A |pgfkeys| collector is very simple: the sole argument of the any key +% macro, regardless of the argument structure of the key, is everything up to +% |\pgfeov|. +\def\advice@pgfkeys@collector#1\pgfeov{% + \AdviceInnerHandler{#1}% +} +% \end{macro} +% +% \subsubsection{Environments} +% \label{sec:code:advice:environments} +% +% \begin{macro}{\advice@activate@env,\advice@deactivate@env} +% Things are simple in \hologo{TeX} and \hologo{ConTeXt}, as their environments +% are really commands. So rather than activating environment name |#2|, we +% (de)activate command |\#2| or |\start#2|, depending on the format. +%<*plain,context> +\def\advice@activate@env#1#2{% + \expanded{% + \noexpand\advice@activate@cmd{#1}{\expandonce{\csname + %start% + #2\endcsname}}% + }% +} +\def\advice@deactivate@env#1#2{% + \expanded{% + \noexpand\advice@deactivate@cmd{#1}{\expandonce{\csname + %start% + #2\endcsname}}% + }% +} +% +% +% We activate commands by redefining them; that's the only way to do it. But +% we won't activate a \hologo{LaTeX} environment |foo| by redefining command +% |\foo|, where the user's definition for the start of the environment actually +% resides, as such a redefinition would be executed too late, deep within the +% group opened by |\begin|, following many internal operations and public +% hooks. We handle \hologo{LaTeX} environments by defining an outer handler +% for |\begin| (consequently, \hologo{LaTeX} environment support can be +% (de)activated by the user by saying (|de|)|activate=\begin|), and +% activating an environment will be nothing but setting a mark, by +% defining a dummy control sequence |\advice@original@csname{#1}{#2}|, +% which that handler will inspect. Note that |force activate| has no +% effect here. +%<*latex> +\def\advice@activate@env#1#2{% + \ifcsdef{\advice@original@csname{#1}{#2}}{% + \advice@activate@error@activated{#1}{#2}{Environment}{already}% + }{% + \csdef{\advice@original@csname{#1}{#2}}{}% + }% +} +\def\advice@deactivate@env#1#2{% + \ifcsdef{\advice@original@csname{#1}{#2}}{% + \csundef{\advice@original@csname{#1}{#2}}{}% + }{% + \advice@activate@error@activated{#1}{#2}{Environment}{not yet}% + }% +} +% \end{macro} +% +% \begin{macro}{\advice@begin@rc} +% This is the handler for |\begin|. It is very special, for speed. It is +% meant to be declared as the run conditions component, and it hacks into +% the normal flow of handling. It knows that after executing the run +% conditions macro, |\advice@handle| eventually (the tracing info may +% interrupt here as |#1|) continues by +% |\advice@handle@rc|\marg{namespace}\marg{handled control sequence}, so it +% grabs all these (|#2| is the \meta{namespace} and |#3| is the +% \meta{handled control sequence}, i.e.\ |\begin|) plus the environment +% name (|#4|). +\def\advice@begin@rc#1\advice@handle@rc#2#3#4{% + % We check whether environment |#4| is activated (in namespace |#2|) by + % inspecting whether activation dummy is defined. If it is not, we execute + % the original |\begin| (|\advice@original@cs{#2}{#3}|), followed by the + % environment name (|#4|). Note that we \emph{don't} execute the + % environment's bailout handler here: we haven't checked its run conditions + % yet, as the environment is simply not activated. + \ifcsname\advice@original@csname{#2}{#4}\endcsname + \expandafter\advice@begin@env@rc + \else + \expandafter\advice@original@cs + \fi + {#2}{#3}{#4}% +} +% \end{macro} +% +% \begin{macro}{\advice@begin@env@rc} +% Starting from this point, we essentially replicate the workings of +% |\advice@handle|, adapted to \hologo{LaTeX} environments. +\def\advice@begin@env@rc#1#2#3{% + % We first load the stage-one configuration for environment |#3| in namespace + % |#1|. + \advice@init@i{#1}{#3}% + % This defined |\AdviceRunConditions| for the environment. We can now check its + % run conditions. If they are not satisfied, we bail out by executing the + % environment's bailout handler followed by the original |\begin| + % (|\advice@original@cs{#1}{#2}|) plus the environment name (|#3|). + \AdviceRunConditions + \ifAdviceRun + \expandafter\advice@begin@env@outer + \else + \AdviceBailoutHandler + \expandafter\advice@original@cs + \fi + {#1}{#2}{#3}% +} +% \end{macro} +% +% \begin{macro}{\advice@begin@env@outer} +% We define the macros expected by the outer handler, see +% |\advice@handle@outer|, load the second-stage configuration, and execute the +% environment's outer handler. +\def\advice@begin@env@outer#1#2#3{% + \def\AdviceNamespace{#1}% + \def\AdviceName{#3}% + \def\AdviceReplaced{#2{#3}}% + \def\AdviceOriginal{\AdviceGetOriginal{#1}{#2}{#3}}% + \advice@init@I{#1}{#3}% + \AdviceOuterHandler +} +% +% \end{macro} +% +% +% \subsubsection{Error messages} +% +% Define error messages for the entire package. Note that +% |\advice@(de)activate@error@...| implement |try activate|. +% +\def\advice@activate@error@activated#1#2#3#4{% + \ifadvice@activate@try + \else + \PackageError{advice (#1)}{#3 "\string#2" is #4 activated}{}% + \fi +} +\def\advice@activate@error@undefined#1#2#3#4{% + \ifadvice@activate@try + \else + \PackageError{advice (#1)}{% + #3 "\string#2" you are trying to #4activate is not defined}{}% + \fi +} +\def\advice@deactivate@error@changed#1#2{% + \ifadvice@activate@try + \else + \PackageError{advice (#1)}{The definition of "\string#2" has changed since we + have activated it. Has somebody overridden our command?}{If you have tried + to deactivate so that you could immediately reactivate, you may want to try + "force activate".}% + \fi +} +\def\advice@error@advice@notcs#1#2{% + \PackageError{advice}{The first argument of key "#1" should be either a single + control sequence or an environment name, not "#2"}{}% +} +\def\advice@error@activate@notcsorenv#1#2{% + \PackageError{advice}{Each item in the value of key "#1activate" should be + either a control sequence or an environment name, not "#2".}{}% +} +\def\advice@error@storecs@notcs#1#2{% + \PackageError{advice}{The value of key "#1" should be a single control sequence, + not "\string#2"}{}% +} +\def\advice@error@noinnerhandler#1{% + \PackageError{advice (\AdviceNamespace)}{The inner handler for + "\expandafter\string\AdviceName" is not defined}{}% +} +% \subsubsection{Tracing} +% +% \begin{macro}{\AdviceTracingOn,\AdviceTracingOff} +% We implement tracing by adding the tracing information to the handlers +% after we load them. So it is the handlers themselves which, if and when +% they are executed, will print out that this is happening. +\def\AdviceTracingOn{% + \let\advice@init@i\advice@trace@init@i + \let\advice@init@I\advice@trace@init@I +} +\def\AdviceTracingOff{% + \let\advice@init@i\advice@setup@init@i + \let\advice@init@I\advice@setup@init@I +} +% \end{macro} +\def\advice@trace#1{\immediate\write16{[tracing advice] #1}} +\def\advice@trace@init@i#1#2{% + \advice@trace{Advising \detokenize\expandafter{\string#2} (\detokenize{#1})}% + \advice@setup@init@i{#1}{#2}% + \edef\AdviceRunConditions{% + % We first execute the original run conditions, so that we can show the + % result. + \expandonce\AdviceRunConditions + \noexpand\advice@trace{\space\space + Executing run conditions: + \detokenize\expandafter{\AdviceRunConditions} + --> + \noexpand\ifAdviceRun true\noexpand\else false\noexpand\fi + }% + }% + \edef\AdviceBailoutHandler{% + \noexpand\advice@trace{\space\space + Executing bailout handler: + \detokenize\expandafter{\AdviceBailoutHandler}}% + \expandonce\AdviceBailoutHandler + }% +} +\def\advice@trace@init@I#1#2{% + \advice@setup@init@I{#1}{#2}% + \edef\AdviceOuterHandler{% + \noexpand\advice@trace{\space\space + Executing outer handler: + \detokenize\expandafter{\AdviceOuterHandler}}% + \expandonce\AdviceOuterHandler + }% + \edef\AdviceCollector{% + \noexpand\advice@trace{\space\space + Executing collector: + \detokenize\expandafter{\AdviceCollector}}% + \noexpand\advice@trace{\space\space\space\space + argument specification: + \detokenize\expandafter{\AdviceArgs}}% + \noexpand\advice@trace{\space\space\space\space + options: + \detokenize\expandafter{\AdviceCollectorOptions}}% + \noexpand\advice@trace{\space\space\space\space + raw options: + \detokenize\expandafter{\AdviceRawCollectorOptions}}% + \expandonce\AdviceCollector + }% + % The tracing inner handler must grab the provided argument, if it's to show + % what it is. + \edef\advice@inner@handler@trace##1{% + \noexpand\advice@trace{\space\space + Executing inner handler: + \detokenize\expandafter{\AdviceInnerHandler}}% + \noexpand\advice@trace{\space\space\space\space + Received arguments: + \noexpand\detokenize{##1}}% + \noexpand\advice@trace{\space\space\space\space + options: + \detokenize\expandafter{\AdviceOptions}}% + \expandonce{\AdviceInnerHandler}{##1}% + }% + \def\AdviceInnerHandler{\advice@inner@handler@trace}% +} +%\resetatcatcode +%\stopmodule +%\protect +% +% +% \subsubsection{The \TikZ; collector} +% \label{sec:code:advice:tikz} +% +% In this section, we implement the argument collector for command |\tikz|, +% which has idiosyncratic syntax, see \PGFmanual{12.2.2}: +% \begin{itemize} +% \item |\tikz|\meta{animation spec}|[|\meta{options}|]{|\meta{picture code}|}| +% \item |\tikz|\meta{animation spec}|[|\meta{options}|]|\meta{picture command}|;| +% \end{itemize} +% where \meta{animation spec} = +% (|:|\meta{key}|={|\meta{value}|}|)*. +% +% The \TikZ; code resides in a special file. It is meant to be |\input| at any +% time, so we need to temporarily assign |@| category code 11. +%<*tikz> +\edef\mmzresetatcatcode{\catcode`\noexpand\@\the\catcode`\@\relax}% +\catcode`\@=11 +\def\AdviceCollectTikZArguments{% +% We initialize the token register which will hold the collected arguments, and +% start the collection. Nothing of note happens until \dots + \mmz@temptoks={}% + \mmz@tikz@anim +} +\def\mmz@tikz@anim{% + \pgfutil@ifnextchar[{\mmz@tikz@opt}{% + \pgfutil@ifnextchar:{\mmz@tikz@anim@a}{% + \mmz@tikz@code}}%] +} +\def\mmz@tikz@anim@a#1=#2{% + \toksapp\mmz@temptoks{#1={#2}}% + \mmz@tikz@anim +} +\def\mmz@tikz@opt[#1]{% + \toksapp\mmz@temptoks{[#1]}% + \mmz@tikz@code +} +\def\mmz@tikz@code{% + \pgfutil@ifnextchar\bgroup\mmz@tikz@braced\mmz@tikz@single +} +\long\def\mmz@tikz@braced#1{\toksapp\mmz@temptoks{{#1}}\mmz@tikz@done} +\def\mmz@tikz@single#1;{\toksapp\mmz@temptoks{#1;}\mmz@tikz@done} +% \dots\ we finish collecting the arguments, when we execute the inner handler, +% with the (braced) collected arguments is its sole argument. +\def\mmz@tikz@done{% + \expandafter\AdviceInnerHandler\expandafter{\the\mmz@temptoks}% +} +\mmzresetatcatcode +% +% \end{macrocode} +% +% Local Variables: +% TeX-engine: luatex +% TeX-master: "doc/memoize-code.tex" +% TeX-auto-save: nil +% End: \ No newline at end of file diff --git a/macros/generic/advice/advice.ins b/macros/generic/advice/advice.ins new file mode 100644 index 0000000000..b1b1df68ce --- /dev/null +++ b/macros/generic/advice/advice.ins @@ -0,0 +1,33 @@ +%% advice.ins +%% +%% This file is a part of Advice, a TeX package implementing a generic +%% framework for extending the functionality of selected commands and +%% environments, available at https://ctan.org/pkg/advice and +%% https://github.com/sasozivanovic/advice. +%% +%% Copyright (c) 2023- Saso Zivanovic +%% (Sa\v{s}o \v{Z}ivanovi\'{c}) +%% +%% This work may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License, either version 1.3c of this license or (at +%% your option) any later version. The latest version of this license is in +%% https://www.latex-project.org/lppl.txt and version 1.3c or later is part of +%% all distributions of LaTeX version 2008 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% The Current Maintainer of this work is Saso Zivanovic. +%% +%% The files belonging to this work and covered by LPPL are listed in `FILES`. +%% +\input docstrip.tex +\keepsilent +\preamble +\endpreamble +\askforoverwritefalse +\generate{% + \file{advice.sty}{\from{advice.dtx}{main,latex}}% + \file{advice.tex}{\from{advice.dtx}{main,plain}}% + \file{t-advice.tex}{\from{advice.dtx}{main,context}}% + \file{advice-tikz.code.tex}{\from{advice.dtx}{tikz}}% +} +\endbatchfile -- cgit v1.2.3