0

Is there any kind of straightforward method of telling tex to somehow run a command on certain page-numbers?

Before I go on, I should state that I'm using the memoir package to format my document.

In particular, what I want to achieve is to have an on-the-fly list generated in the margin that is populated with small mathematical equations or short snippets of text (these equations or text-snippets are defined somewhere in the main body of the document via a command that is elucidated below). Specifically, I want the first appearance of the list to be in the margin of the same page that the first list-item is created on. I then want the same list to be generated on every second page (i.e., every even or every odd page, depending on where the first instance of the list popped up) until a page is reached on which a new list-item has been created, at which point I want all subsequent lists to be populated with both items. This process should then be repeated, picking up new list-items as it goes, until the end of the current section.

To highlight with a specific example, let's say I create list entries on pages 1, 2, 7, and 8. Then I should have the following:

  • Page 1 margin-list: one item.
  • Page 3 margin-list: two items.
  • Page 5 margin-list: two items.
  • Page 7 margin-list: three items.
  • Page 9 margin-list: four items.

So far I've managed to cobble together (thanks in large to questions on this very site) a few commands that take care of most of what I need. I have a command-building command that can be used – in conjunction with a counter – to create indexed commands which contain my list-entries:

\newcounter{CountItems}[section] % This creates a counter that counts how many list-items have appeared in a given section.

\newcommand\addindex[4][g]{\csname #1def\expandafter\endcsname\csname #2\roman{#3}\endcsname{#4}} % This is a command-creating command that uses concatenation to create indexed commands using a counter. #2 is the root-name of the function, #3 uses a counter to create an index to be appended to the root-name, #4 is the content stored by the command, and #1 is used to modify the behaviour of the "def" command.

\newcommand{\DeriveCond}[3]{\stepcounter{CountItems}{\addindex{conditem}{CountItems}{#1}}} % This command takes as input #1, which is the actual text to appear in the list. Using the above command (\addindex), this command creates indexed commands "conditemi", "conditemii", "conditemiii", etc, that contain the content for the list.

I can then construct a list on any page in my document using a for-loop in an itemize environment. I mentioned at the start that I'm using the memoir class, so I can wrap the itemize environment with \sidebar{} to produce a command – marginlist – that generates an up-to-date margin-list on whichever page it is called on:

\newcounter{itemloopcounter}[section] % This creates a counter to be used in list-generation. This is a dummy-counter created for use in the loop in the next command. It keeps track of the value of the loop variable \cond so that each of the "contitem" commands can be called easily.

\newcommand{\marginlist}[1]{
\sidebar{
\begin{itemize}{\usecounter{itemloopcounter}}
        \foreach \cond in {1,...,\value{CountItems}}
        {
            \stepcounter{itemloopcounter}
            \item[\roman{itemloopcounter})] \csname conditem\roman{itemloopcounter}\endcsname
        }
    \end{itemize}
    \setcounter{itemloopcounter}{0}
}
}

However, at this point, I must manually run this command on every page that I want the list to appear on; this can be annoying if I subsequently include new content/remove old content in-between two instances of \marginlist as I might then have to find somewhere new to execute the latter instance of the command so that it's on the correct page (either left or right). Not a big deal, but something that could go overlooked in the editing process. I'm somewhat lost on where to look next in order to achieve the type of automation that I want.

I'm not too familiar with the inner-workings of tex (this is probably the deepest I've ever delved into it), so I took a stab in the dark and assumed that maybe the command \sidebar{} uses \thepage in order to figure out where to build the environment; I locally (and temporarily) changed the value of \thepage within my \marginlist command in the hopes that I could fool tex into thinking that the command had been executed on a different page, but alas, tex appears to be smarter than this.

Any pointers on how this can be achieved would be appreciated.

  • Welcome to TeX.SX! Can you please expand the code snippet that you have posted to a full minimal working example. It is much easier to help you if we can start with some compilable code that illustrates your problem. A MWE should start with a \documentclass command, include any necessary packages and be as small as possible to demonstrate your problem. – Andrew Dec 23 '19 at 20:01
0

To automate this you need to automatically add your list to each page, which you can do using the \AtBeginShipout from the atbegshi package. (The primitive TeX \shipout is used to construct each and every page that (La)TeX produces; see All about \shipout for more details.)

The code below that does what you want using LaTeX3 sequences to store the list of side bar entries. I didn't use your code partly because you didn't provide a minimal working example but mostly because I think that LaTeX3 gives a nicer programming interface for doing this sort of thing.

In terms of usage all that you need to know is the command \AddListEntry, which is used to add a list entry to the sidebar. Using this the top of the fourth page of the MWE below looks like:

enter image description here

Here is the full code:

\documentclass[a4paper, twoside]{memoir}
\usepackage{lipsum}

\usepackage{expl3}
% track the section of the current list, resetting when section changes
\newcounter{listsection}[section]

\ExplSyntaxOn
\seq_new:N \l_list_entries_seq% holds the sequence of sidebar entries
\int_new:N \l_list_page_int% page number of first entry in section

% add the current side list to the sidebar if it is non-empty and
% the page number has the same parity as \l_list_page_int
\newcommand\PrintPageList{
  \seq_if_empty:NF \l_list_entries_seq
  { % only print the list if it is non-empty
    \int_if_even:nT { \thepage - \int_use:N \l_list_page_int }
    { % if page number has same parity as list start page
      \sidebar{
        \begin{itemize}
          \item\seq_use:Nn \l_list_entries_seq {\item}
        \end{itemize}
      }
    }
  }
}

% reset the sidebar list - not strictly necessary as the list resets
% automatically but this gives a way of manually resetting the list
\newcommand\ResetList
{
    \seq_clear:N \l_list_entries_seq
}

% add a sidebar list entry
\newcommand\AddListEntry[1]
{
    % reset the sidebar list if the section has changed
    \int_compare:nT { \arabic{section} != \arabic{listsection}}
    {
        \ResetList
    }
    \seq_if_empty:NT \l_list_entries_seq {
        % record the starting page if sequence is empty
        \int_set:Nn \l_list_page_int {\thepage}
        % set listsection to reset the list in a new section
        \setcounter{listsection} {\arabic{section}}
    }
    \seq_put_right:Nn \l_list_entries_seq {#1}
}
\ExplSyntaxOff

% add the list to every page
\usepackage{atbegshi}
\AtBeginShipout{\PrintPageList}

\begin{document}

  \chapter{First chapter}
  \section{First section}

   \AddListEntry{One: $1$}
   \lipsum

   \AddListEntry{Two $2$}
   \lipsum

   \AddListEntry{Three $3$}
   \lipsum

   \section{Second section}

   \AddListEntry{Four $4$}
   \lipsum

   \AddListEntry{Five $5$}
   \lipsum

\end{document}

There are some comments in the code that explain what is happening. The main idea is that the \AddListEntry command add the new list entry to the sequence \l_list_entries_seq. Before doing this it checks to see if a new section has started and, if so, resets the list. This macro also keeps track of the page number of the first list entry in the section and the section number.

The other main command is \PrintPageList, which is used on every page because of \AtBeginShipout{\PrintPageList}. The \PrintPageList checks the parity of the current page number against the page number of the first list entry and if the parity and \l_list_entries_seq is not empty it inserts a \sidebar list.

|improve this answer|||||
  • This is exactly what I was looking for! I'm pleasantly surprised to have found a solution so quickly; you've saved me a lot of time that would have otherwise been spent banging my head on my desk trying to make this work via some other—no doubt horrendously complicated—way. Thank you very much for the help, Andrew! – ScottTheScot Dec 28 '19 at 12:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.