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.

jan
  • 2,236
  • 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 07 '19 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 07 '19 at 12:16

1 Answers1

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.

frougon
  • 24,283
  • 1
  • 32
  • 55
  • Works perfectly, thank you for the detailed explanation! – jan Apr 07 '19 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 07 '19 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 '19 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 '19 at 21:17
  • ... the intricacies of markup :) – jan Apr 10 '19 at 21:31
  • Yep, now I have bookmarked how-do-i-mark-inline-code. It took me some time to find again, but saved my life. :) Did you understand my explanation? Do you understand \let? Because it's the main actor regarding your question. – frougon Apr 10 '19 at 21:33
  • I do. I had forgotten that I had that option keeprepetitions :P Concerning \let, yes, I wasn't aware of that trick but it's neat. I might want to keep the option to use the command outside of the environment though (without any of the extra stuff), so I'll probably not use it though. It shouldn't break anything else, should it? – jan Apr 10 '19 at 21:39
  • You can skip the indirection through \my@command in this case, I don't think it will cause any problem. Except maybe that the counter is reset to 0 only when the environment is entered, so repetitions may be skipped if you use \mycommand out of the environment. But maybe this is what you expect, after all. – frougon Apr 10 '19 at 21:45
  • Sorry to bug you again: I'm trying to incorporate the code now into a larger framework and for some weird reason, when issuing the command nested in some other environments, it ceases to work. Any ideas why. Here is my project https://www.overleaf.com/1791495566wqkfvrtrnymd which is based on this framework for linguists ... – jan Apr 10 '19 at 22:09
  • Hmmm, I have no account at Overleaf and will be off for tonight, sorry. Try to make a MWE, as usual... – frougon Apr 10 '19 at 22:13
  • Oh, I see, ok, I'm sure the community will have some ideas ;) – jan Apr 10 '19 at 22:16
  • Just FYI, the issue I had was that I needed to use gdef to update the stored value globally. See here – jan Apr 11 '19 at 09:25
  • Thanks for the heads-up and glad to see you could sort it out! – frougon Apr 11 '19 at 09:54