1

Based on this question, I have created a simple macro that prints and stores its argument, and if it is issued again with the identical argument it prints nothing:

% !TEX TS-program = xelatexmk
\RequirePackage{filecontents}

\begin{filecontents}{mypackage.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}

\newenvironment{myenv}{\ignorespaces}{\par\ignorespacesafterend}

\DeclareOption{skiprepetitions}{%
  \AtBeginDocument{\let\mypackage@print\mypackage@print@skip}%
}
\DeclareOption{keeprepetitions}{%
  \AtBeginDocument{\let\mypackage@print\mypackage@print@keep}%
}
\ExecuteOptions{keeprepetitions}
\ProcessOptions\relax

\RequirePackage{xifthen}

\def\mypackage@storedval{} % create a macro to later store a value in

\newcommand{\mycommand}[1]{%
  \ifthenelse{\isempty{#1}}
    {% if empty just print an empty line
     \mbox{}%
    }
    {% if not empty
     \mypackage@print{#1}%
     \def\mypackage@storedval{#1}%
    }%
}

\def\mypackage@print@skip#1{%
  \ifthenelse{\equal{#1}{\mypackage@storedval}}
   {\mbox{}}
   {#1}%
}
\def\mypackage@print@keep#1{#1\\}

\end{filecontents}

\documentclass{article} 

\usepackage[skiprepetitions]{mypackage}

\begin{document}

\begin{myenv}
\mycommand{A} \\ %A
\mycommand{A} \\  %empty
\mycommand{A} \\  %empty
\mycommand{} \\    %empty
\mycommand{B} \\  %B
\mycommand{C} \\  %C
\mycommand{C} \\  %empty
\mycommand{A} \\  %A
\end{myenv}

\begin{myenv}
\mycommand{A} \\ %A  
\mycommand{A} \\ %empty

\newpage

\mycommand{A} \\ %A 
\end{myenv}

\end{document}

I would like to now have it "reset" or "forget" what is stored at pagebreaks and boundaries of a specific environment, so that on a new page, or in a new environment it does print its argument even if the previous one was identical.

Outcommented in the document above are the values that I expect to see.

  • 1
    in general (unless all your page breaks are forced) you would need a two pass system leaving markers and checking page numbers, eg via \pageref. Fortunately there is a package for do most of the work for you, see the perpage package. – David Carlisle Apr 7 at 10:50
  • @DavidCarlisle Hmm, I don't quite understand how though. So perpage seems to reset counters, but how do I get it to reset the value that I'm storing in that macro? In general even at environment boundaries I don't know how to reset it. – jan Apr 7 at 12:16
1

As far as I understood, the following does what you want (thanks to David Carlisle's comment for the hint about perpage!):

\RequirePackage{filecontents}

\begin{filecontents}{mypackage.sty}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{mypackage}

\RequirePackage{perpage}

\newcounter{mycounter}
\MakePerPage{mycounter}

\newenvironment{myenv}{%
  % \mycommand is only defined inside the environment
  \let\mycommand\my@command
  % Make sure the next \mycommand isn't ignored, even if its argument
  % matches the value stored in \mypackage@storedval.
  \setcounter{mycounter}{0}%
  \ignorespaces
}{%
  \par\ignorespacesafterend
}

\DeclareOption{skiprepetitions}{%
  \AtBeginDocument{\let\mypackage@print\mypackage@print@skip}%
}
\DeclareOption{keeprepetitions}{%
  \AtBeginDocument{\let\mypackage@print\mypackage@print@keep}%
}
\ExecuteOptions{keeprepetitions}
\ProcessOptions\relax

\RequirePackage{xifthen}

\def\mypackage@storedval{} % create a macro to later store a value in

\newcommand{\my@command}[1]{%
  \ifthenelse{\isempty{#1}}%
    {% if empty just print an empty line
     \mbox{}%
    }%
    {% If this is the first \stepcounter{mycounter} executed since the current
     % page was started, this sets 'mycounter' to 1.
     \stepcounter{mycounter}%
     \mypackage@print{#1}%
     \def\mypackage@storedval{#1}%
    }%
}

\def\mypackage@print@skip#1{%
  \ifthenelse{\cnttest{\value{mycounter}}>{1}\AND
              \equal{#1}{\mypackage@storedval}}%
   {\mbox{}}%
   {#1}%
}
\def\mypackage@print@keep#1{#1\\}

\end{filecontents}

\documentclass{article}

\usepackage[skiprepetitions]{mypackage}

\begin{document}
\setlength{\parindent}{0pt}

\begin{myenv}
\mycommand{A}\\  %A
\mycommand{A}\\  %empty
\mycommand{A}\\  %empty
\mycommand{}\\   %empty
\mycommand{B}\\  %B
\mycommand{C}\\  %C
\mycommand{C}\\  %empty
\mycommand{A}\\  %A
\end{myenv}

\begin{myenv}
\mycommand{A}\\ %A
\mycommand{A}\\ %empty

\newpage

\mycommand{A}\\ %A
\end{myenv}

\end{document}

I used a new counter called mycounter that is subject to \MakePerPage{mycounter}. This implies that the first \stepcounter{mycounter} on any given page resets mycounter to 1,a because 1 is the default value of the optional argument of \MakePerPage.

I made it so that at the beginning of myenv, mycounter is set to 0. Besides, \stepcounter{mycounter} is executed whenever \mycommand is called with a non-empty argument. Therefore, after this \stepcounter{mycounter} is performed, mycounter is equal to 1 if and only if:

  • this is the first \mycommand call on the current page, or;

  • this is the first \mycommand call in the current myenv environment.

This is the basis for the “ignoring criterion” you need. The only other thing to check is that, when we are in “possibly-ignoring mode”,b the argument passed to \mycommand differs from the saved value (since all this comes after the emptyness check).

Note that I renamed your \mycommand to \my@command and do \let\mycommand\my@command only inside the myenv environment, so that if you have a call to \mycommand that lies out of any myenv environment, LaTeX will report an error (undefined command).

You may want to move the \stepcounter{mycounter} call at the beginning of the \my@command macro: this depends on the behavior you want when an empty argument is passed. Should it make the new state be “possibly-ignoring mode”, or should it let the state unchanged (in my code: the latter)? This is a decision that belongs to you. But since you just output an \mbox{} for empty arguments, the difference won't be very visible in general...

enter image description here

Footnotes


a. After two compilation runs, since the perpage package relies on the .aux file.

b. I.e., the argument is non-empty and the \mycommand call is neither the first-on-page nor the first-in-environment.

  • Works perfectly, thank you for the detailed explanation! – jan Apr 7 at 18:59
  • Glad to hear that. I added a little discussion on the handling of empty arguments, because there is a policy decision to make that I believe wasn't precisely specified in the question (or else, I missed it). – frougon Apr 7 at 19:05
  • Quick further question: What exactly does \def\mypackage@print@keep#1{#1\\} do? (right before \end{filecontents}) I'm trying to understand the code and that part is still obscure to me (outcommenting doesnt' seem to have an effect) ... – jan Apr 10 at 20:50
  • 1
    It just prints its argument followed by \\ (some sort of carriage return). I suppose you knew that. Now, in the big picture of your .sty, it ensures that when keeprepetitions is used as a package option (and isn't followed by skiprepetitions), \mypackage@print is a very dumb macro that just prints its argument and then starts a new line. In other words, the option keeprepetitions uses this macro (indirectly: \mycommand\my@command\mypackage@print\mypackage@print@keep) to disable all the smart logic, except the check for emptyness of the argument. – frougon Apr 10 at 21:17
  • ... the intricacies of markup :) – jan Apr 10 at 21:31

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.