summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/hereapplies/hereapplies.sty
blob: 734881ca8f880077060258f490c093cbc05a0681 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
%  -*- Mode: latex; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-

%
% hereapplies.sty
%
% A LaTeX package for cross-linking concepts to their applications
%
% https://github.com/madmurphy/hereapplies.sty
%
% Version 0.3.0
%
% Copyright (c) 2022 madmurphy <madmurphy333@gmail.com>
%
% **Here Applies** is free software: you can redistribute it and/or modify it
% under the terms of the GNU Affero General Public License as published by the
% Free Software Foundation, either version 3 of the License, or (at your
% option) any later version.
%
% **Here Applies** is distributed in the hope that it will be useful, but
% WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
% for more details.
%
% You should have received a copy of the GNU Affero General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
%
%
%
% Example usage:
%
%     \documentclass{article}
%
%     \usepackage{hereapplies}
%
%     \begin{document}
%
%     \title{Some title}
%     \author{Some author}
%
%     \maketitle
%
%     This is concept one. To find this concept applied, please
%     see \whereapplies{conceptOne}.
%
%     This is concept two. To find this concept applied, please
%     see \whereapplies{conceptTwo}.
%
%     \newpage
%
%     \hereapplies{conceptOne}This is page \thepage. As you can see, ``concept
%     one'' applies here.\newpage
%
%     \hereapplies{conceptTwo}This is page \thepage. As you can see,
%     ``concept two'' applies here.\newpage
%
%     \hereapplies{conceptOne}This is page \thepage. As you can see, ``concept
%     one'' applies here.\newpage
%
%     \hereapplies{conceptTwo}This is page \thepage. As you can see,
%     ``concept two'' applies here.\newpage
%
%     \hereapplies[myref]{conceptOne}This is page \thepage. As you can
%     see, ``concept one'' applies here. This point in the document is
%     labeled \texttt{myref}.
%
%     \end{document}
%
%
\ProvidesPackage{hereapplies}
\RequirePackage{hyperref}
%
%
%
%         PRIVATE ENVIRONMENT
%         ===================
%
%
% Assign a unique number to each applicable concept
\newcounter{@ha@concept@counter}
% Assign a unique number to each unnamed occurrence of a concept
\newcounter{@ha@unlabeled@counter}
%
%
% Macro `\@ha@newapplicable{concept_name}`
% *****************************************************************************
%
% Initialize a new applicable concept
%
% Thie macro is for internal purposes only. When invoked, it sets up the helper
% macros, counters and auxiliary files needed for keeping track of a concept.
% If the concept was already initialized this macro will be no op.
%
% The `concept_name` argument may contain only letters and the "at" sign (`@`).
%
\newcommand{\@ha@newapplicable}[1]{%
	% Was this concept already initialized?
	\expandafter\ifx\csname @ha@prop@@cursor@#1\endcsname\relax%
		% The concept was never initialized
		% Move the counter to the current id
		\stepcounter{@ha@concept@counter}%
		% Set the output to "??" - it will be updated by the .haN file
		\expandafter\def\csname @ha@prop@@uoutput@#1\endcsname{\textbf{??}}%
		% Set the starred output to "??" - it will be updated by the .haN file
		\expandafter\def\csname @ha@prop@@soutput@#1\endcsname{\textbf{??}}%
		% The last page that applies
		\expandafter\def\csname @ha@prop@@cursor@#1\endcsname{-1}%
		% Store the id of the current concept
		\expandafter\edef\csname @ha@prop@@id@#1\endcsname{\arabic{@ha@concept@counter}}%
		% Initialize the .haN file
		% Previous versions had this line, which created unwanted whitespaces:
		%\@starttoc{ha\csname @ha@prop@@id@#1\endcsname}%
		% I am thankful to David Carlisle for the following replacement:
		{\endlinechar=\m@ne\@starttoc{ha\csname @ha@prop@@id@#1\endcsname}}%
		% Store all the occurrences when the document reaches the end
		\AtEndDocument{%
			% Make sure that there are occurrences
			\expandafter\ifcsname @ha@prop@@preamble@#1\endcsname%
				% There are occurrences
				% When `\hereapplies` is the last directive of the document and
				% is not followed by any text, due to an unknown bug
				% `\whereapplies` prints "??" even when there are occurrences
				% of a concept. Uncommenting the `\phantom` macro below fixes
				% the bug, yet it might be wiser to leave the choice to the
				% user and search for more final solutions in the meanwhile.
				% The `\phantom{}` directive can also be inserted
				% manually at the end of the document.
				%\phantom{}%
				% Set the .haN file as output
				\addtocontents{ha\csname @ha@prop@@id@#1\endcsname}{%
					% Write "p./pp. \pageref..." to the output
					\gdef\expandafter\protect\csname @ha@prop@@uoutput@#1\endcsname{%
						\csname @ha@prop@@preamble@#1\endcsname\csname @ha@prop@@ucache@#1\endcsname%
					}%
					% Write "p./pp. \pageref*..." to the starred output
					\gdef\expandafter\protect\csname @ha@prop@@soutput@#1\endcsname{%
						\csname @ha@prop@@preamble@#1\endcsname\csname @ha@prop@@scache@#1\endcsname%
					}%
				}%
			\fi%
		}%
	\fi%
}
%
%
% Macro: `\@ha@getpropat{property_name}{concept_name}`
% *****************************************************************************
%
% Get the current value of a concept's property
%
% Thie macro is for internal purposes (but nothing forbids invoking it
% directly).
%
% Possible values for the `property_name` argument are: `cursor`, `id`,
% `preamble`, `scache`, `soutput`, `ucache`, `uoutput`.
%
% The `concept_name` argument may contain only letters and the "at" sign (`@`).
%
\newcommand{\@ha@getpropat}[2]{%
	% Make sure that the applicable concept is initialized
	\@ha@newapplicable{#2}%
	% Print all the occurrences of the concept
	\csname @ha@prop@@#1@#2\endcsname%
}
%
%
% Macro: `\starred@labeled@hereapplies{label}{concept_name}`
% *****************************************************************************
%
% Equivalent to `\hereapplies*`, but the two arguments are both mandatory
%
% Thie macro is for internal purposes (but nothing forbids invoking it
% directly). See the documentation of `\hereapplies` for more information.
%
\newcommand{\starred@labeled@hereapplies}[2]{%
	% Make sure that the concept has been initialized
	\@ha@newapplicable{#2}%
	% Assign a label to this occurrence
	\label{#1}%
	% If the cursor already points to the current page do nothing
	\unless\ifnum\csname @ha@prop@@cursor@#2\endcsname=\thepage%
		% Is this the first occurrence?
		\expandafter\ifcsname @ha@prop@@preamble@#2\endcsname%
			% This is *not* the first occurrence
			% Use "pp." for the preamble when there are multiple occurrences
			\expandafter\def\csname @ha@prop@@preamble@#2\endcsname{pp.~}%
			% Populate the cache
			\expandafter\g@addto@macro\csname @ha@prop@@ucache@#2\endcsname{, \pageref{#1}}%
			% Populate the starred cache
			\expandafter\g@addto@macro\csname @ha@prop@@scache@#2\endcsname{, \pageref*{#1}}%
		\else%
			% This is the first occurrence
			% Use "p." for the preamble when there is only one occurrence
			\expandafter\def\csname @ha@prop@@preamble@#2\endcsname{p.~}%
			% Initialize the cache
			\expandafter\def\csname @ha@prop@@ucache@#2\endcsname{\pageref{#1}}%
			% Initialize the starred cache
			\expandafter\def\csname @ha@prop@@scache@#2\endcsname{\pageref*{#1}}%
		\fi%
		% Make the cursor point to the current page
		\expandafter\edef\csname @ha@prop@@cursor@#2\endcsname{\thepage}%
	\fi%
}
%
%
% Macro: `\starred@hereapplies[label]{concept_name}`
% *****************************************************************************
%
% Equivalent to `\hereapplies*`
%
% Thie macro is for internal purposes (but nothing forbids invoking it
% directly). See the documentation of `\hereapplies` for more information.
%
\newcommand{\starred@hereapplies}[2][]{%
	% Check whether the macro has been called with one or two arguments
	\ifx&#1&%
		% The macro has been called with only one argument
		% Assign a unique number to the unnamed occurrence
		\stepcounter{@ha@unlabeled@counter}%
		% Call `\starred@hereapplies` with an opaque label
		\edef\tmp{\noexpand\starred@labeled@hereapplies{#2:__appl\arabic{@ha@unlabeled@counter}__}{#2}}\tmp%
	\else%
		% The macro has been called with two arguments
		\starred@labeled@hereapplies{#1}{#2}%
	\fi%
}
%
%
%
%         PUBLIC ENVIRONMENT
%         ===================
%
%
% Macro: `\hereapplies[label]{concept_name}`
% *****************************************************************************
%
% Notify the document that a particular concept applies and create a label for
% it
%
% If the optional argument is passed the label created will be named
% accordingly, otherwise an opaque name will be assigned to it. This argument
% may contain only what is legal in both `\label` and `\pageref`.
%
% The `concept_name` argument may contain only letters and the "at" sign (`@`).
%
% The starred version of this macro (`\hereapplies*`) does not invoke the
% `\phantomsection` directive.
%
\newcommand{\hereapplies}{%
	% Check if a star is present in the invocation of the command
	\@ifstar{\starred@hereapplies}{\phantomsection\relax\starred@hereapplies}%
}
%
%
% Macro: `\whereapplies{concept_name}`
% *****************************************************************************
%
% Print all the occurrences of a concept in the form "p. ..." or "pp. ..."
%
% The `concept_name` argument may contain only letters and the "at" sign (`@`).
%
% The starred version of this macro (`\whereapplies*`) will use `\pageref*`
% instead of `\pageref` for generating the page list.
%
\newcommand{\whereapplies}{%
	% Check if a star is present in the invocation of the command
	\@ifstar{\@ha@getpropat{soutput}}{\@ha@getpropat{uoutput}}%
}