Is it possible to push or pop a length like parindent? I want to change it temporarily but reset it soon after.
I have a solution but looking for something a little nicer. I'll post it as an answer.
Is it possible to push or pop a length like parindent? I want to change it temporarily but reset it soon after.
I have a solution but looking for something a little nicer. I'll post it as an answer.
Just say, in your preamble,
\newlength{\savedparindent}
and, when you want to change the \parindent, say
\setlength{\savedparindent}{\parindent}
\setlength{\parindent}{<dimen>}
Then you can restore the previous \parindent by
\setlength{\parindent}{\savedparindent}
This is even unnecessary if you use the environment structure:
\newenvironment{otherparindent}[1]
{\par\setlength{\parindent}{#1}}
{\par}
so that the change to \parindent is confined to the environment. Of course it's not necessary to use such an environment: you can put the \setlength{\parindent}{<dimen>} code in the definition of any environment.
A stack based solution is easily obtained with LaTeX3:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \g_uiy_parindent_seq
\NewDocumentCommand{\pushparindent}{m}
{
\seq_gpush:Nx \g_uiy_parindent_seq {\the\parindent}
\dim_gset:Nn \parindent {#1}
}
\NewDocumentCommand{\popparindent}{}
{
\seq_gpop:NN \g_uiy_parindent_seq \l_tmpa_tl
\dim_gset:Nn \parindent {\l_tmpa_tl}
}
\ExplSyntaxOff
\begin{document}
\showthe\parindent
\pushparindent{100pt}
\showthe\parindent
\pushparindent{50pt}
\showthe\parindent
\popparindent
\showthe\parindent
\popparindent
\showthe\parindent
\end{document}
This will show in succession 15.0pt, 100.0pt, 50.0pt, 100.0pt, 15.0pt; the command \pushparindent stores the current value and sets \parindent to the value specified in the argument. With \popparindent you get the last stored value.
Here's a version with "standard LaTeX":
\newtoks\parindentstack
\newcommand{\pushparindent}[1]{%
\edef\temp{\noexpand\listelement{\the\parindent}\the\parindentstack}%
\global\parindentstack=\expandafter{\temp}%
\global\parindent=#1\relax
}
\newcommand{\popparindent}{%
\if\relax\detokenize\expandafter{\the\parindentstack}\relax
\errmessage{Empty stack}%
\else
\expandafter\getlistelement\the\parindentstack\getlistelement
\fi
}
\def\getlistelement\listelement#1#2\getlistelement{%
\global\parindentstack{#2}\global\parindent=#1\relax}
You can implement a stack without using LaTeX3 as well, by using a token list and some macros. That would work as follows:
\documentclass{article}
\newtoks\paridstack
\paridstack={\empty}
\def\push#1#2{%
\begingroup\toks0={{#1}}%
\edef\act{\endgroup\global#2={\the\toks0 \the#2}}\act
}% push #1 onto #2
\def\pop#1{%
\begingroup
\edef\act{\endgroup\noexpand\splitList\the#1(tail)#1}\act
}% pop from #1
\def\splitList#1#2(tail)#3{%
\ifx#1\empty Can't pop an empty stack.\else#1\global#3={#2}\fi
}
\begin{document}
\noindent
\push{200pt}{\paridstack}%
\pop{\paridstack}\\
\push{200pt}{\paridstack}%
\push{300pt}{\paridstack}%
\push{400pt}{\paridstack}%
\pop{\paridstack}\\
\pop{\paridstack}\\
\pop{\paridstack}\\
\pop{\paridstack}
\end{document}
It will print 200pt 400pt 300pt 200pt Can't pop from an empty stack.. You can of course replace the error message with some error handling code, or some default value to be returned. This is all based on an example in chapter 14 of TeX by Topic. If you want to set \parindent directly instead of just printing the value, simply change #1 in the \else of \splitList to \parindent=#1.
\globals. I think this should be sufficient to handle groups. Of course, the assignment to \parindent would have to be made global as well for it to persist after the end of a group.
– Roelof Spijker
Apr 07 '12 at 13:05
\parindent assignment should be global or not.
– egreg
Apr 07 '12 at 13:11
\toks0 can sometimes be risky. I have therefore edited your post to localize the assignment. Also, \push{\empty}{\paridstack} \pop{\paridstack} fails. You may want to change the marker \empty.
– Ahmed Musa
Apr 08 '12 at 17:06
\toks0 causes problems? As far at the \empty goes, I don't see that as a real problem, considering what the stack is meant for.
– Roelof Spijker
Apr 08 '12 at 17:52
\toks0 you could easily get into trouble when you try to hack or take from that code. Regarding the second issue, since your stack looks like a general-purpose bin, all obvious sources of difficulty should be avoided. Luckily it is easy to do so; see my second answer.
– Ahmed Musa
Apr 08 '12 at 18:39
\empty marker of course.
– Roelof Spijker
Apr 08 '12 at 19:24
You can create a new macro length and add \parindent to it which creates a copy of \parindent
\newlength{\mylen}
\setlength\mylen{\parindent}
\setlength\parindent{0pt}
When you are ready to restore \parindent you can use
\setlength\parindent{\mylen}
Note this method does not allow nesting.
There is also a stack based solution but maybe less efficient.
With lualatex you can simply use a stack in lua with a tex macro wrapper. Should be pretty self-explanatory.
x = a + 0 is the same as x = a. (assuming \mylen is 0)
– Uiy
Apr 07 '12 at 10:44
\addtolength will not work if you use this twice in the same group.
– egreg
Apr 07 '12 at 10:48
\newlength will not do anything if the length is already defined. Hence \setlength is correct. Usually latex complains about creating something new when it already exists. I thought the same would be true here but I guess not. Seems to be an inconsistency in design. Should have a \renewlength and similar stuff to other things.
– Uiy
Apr 07 '12 at 11:08
\newlength{\mylen} only once.
– egreg
Apr 07 '12 at 11:10
\newlength semantics doesn't jive with \newcommand. If I use \newcommand twice on the same macro name it complains... but doesn't do this with \newlength.
– Uiy
Apr 07 '12 at 11:14
\newlength behaves like \newcommand: indeed it doesn't; it just allocates a register to store a length.
– egreg
Apr 07 '12 at 11:18
{} groups or a latex environment. You only need the explicit stack implementations if you need global definitions with a nested structure that doesn't use Tex groups. Needing this isn't that uncommon (for example in tables you often want a logical group corresponding to a row, but you can not use a Tex group there) But your question gives no indication that that is needed in your case.
– David Carlisle
Apr 07 '12 at 16:15
\parindent=3pt { \parindent=10pt } What is the parindent value here? 3pt or 10pt?]
– Uiy
Apr 07 '12 at 21:55
\parindent=3pt { \parindent=10pt } \showthe\parindent all local assignments are restored at the end of a group. You can do \global\parindent=5pt then it is not restored
– David Carlisle
Apr 08 '12 at 08:07
Here is another solution for pushing and popping dimensions. It allows many measures to be pushed or popped at once.
\documentclass{article}
\usepackage{catoptions}
\makeatletter
\robust@def*\pushlengths#1{%
\cptdocommalist{#1}{%
\cptexpanded{%
\gdef\noexpandcsn{pushlength@\cptgobblescape##1}{%
\noexpand\pushstop{##1\the##1\relax}%
\ifcsndefTF{pushlength@\cptgobblescape##1}{%
\ifcsnnullTF{pushlength@\cptgobblescape##1}{%
\noexpand\pushstop{}%
}{%
\expandcsnonce{pushlength@\cptgobblescape##1}%
}%
}{%
\noexpand\pushstop{}%
}%
}%
}%
}%
}
\robust@def*\poplengths#1{%
\cptdocommalist{#1}{%
\cptexpanded{%
\cpt@poplength\expandcsnonce{pushlength@\cptgobblescape##1}%
}%
\pushstopp##1%
}%
}
\robust@def*\cpt@poplength\pushstop#1#2\pushstopp#3{%
\ifblankTF{#1}{%
\cpt@err{Can't pop empty stack
\noexpandcsn{pushlength@\cptgobblescape#3}}\@ehd
}{%
#1\csn@gdef{pushlength@\cptgobblescape#3}{#2}%
}%
}
\makeatother
% Tests:
\begin{document}
\parindent20pt
\pushlengths{\parindent,\textwidth,\textheight,\rightmargin,\leftmargin}
\parindent0pt
\pushlengths\parindent
\poplengths\parindent
\poplengths{\parindent,\textwidth,\textheight,\rightmargin,\leftmargin}
% \poplengths\parindent % -> Can't pop empty stack.
\end{document}
This solution is just a response to Roelof. I hope it is appropriate here. I replace \empty by \roelofstackbegin in his solution. This is safer than using \empty. Roelof's stack looks like an all-purpose stack. So any possible source of failure should be avoided.
\documentclass{article}
\makeatletter
\newtoks\roelofstack
\roelofstack={\roelofstackbegin}
\def\roelofstackbegin{\empty}
% push #1 onto #2:
\def\push#1#2{%
\begingroup
\toks0={{#1}}%
\edef\act{\endgroup\global#2={\the\toks0 \the#2}}%
\act
}
% pop from #1:
\def\pop#1{%
\begingroup
\edef\act{\endgroup\noexpand\SplitOff\the#1(tail)#1}%
\act
}
\def\SplitOff#1#2(tail)#3{%
\ifx#1\roelofstackbegin
\errhelp{Attempting to pop empty stack #3.}%
\errmessage{You can't pop an empty stack.}%
\else
#1\global#3={#2}%
\fi
}
\makeatother
% Tests:
\begin{document}
\noindent
\push{\empty}{\roelofstack}
\pop{\roelofstack}
\push{200pt}{\roelofstack}
\pop{\roelofstack}\par
\push{200pt}{\roelofstack}
\push{300pt}{\roelofstack}
\push{400pt}{\roelofstack}
\pop{\roelofstack}\par
\pop{\roelofstack}\par
\pop{\roelofstack}\par
% \pop{\roelofstack} % Popping empty stack.
\end{document}