2

I would like to know if it is possible to program a function that would accept a given symbol an infinite number of times. The application I have in mind is the following. I would like, if possible, a command \spac which would take two booleans and by default be defined as \vspace*{0.5ex}.

It would take two symbols.

  • The * says if there is a line break or not; thus, \spac* is defined as \\[0.5ex].

  • The presence of one + means that there will be a 0.5ex \vspace increment.

  • The specificity here, is that we should be able to add as many + as we want, or, if not possible (this is part of the question, to determine whether it is possible or not), a reasonably high number of them such as 5 or 6.

To sum up, each + will add 0.5ex to the total vertical space. The vertical space will thus always be equal to 0.5*(n+1)ex, n symbolizing the number of +s.

I would like the +(s) to be written directly without separators, just like a t+ argument type.

Examples:

  • \spac+++++ will produce a vspace of 0.5*6 = 3ex, without a line break.
  • \spac*++ will produce a line break and a vspace of 0.5*3 = 1.5ex.

Context

\documentclass{article}
\usepackage{linguex}
\usepackage{etoolbox}

\NewDocumentCommand{\tslt}{m}{`#1'}

  \makeatletter

\patchcmd{\a}{\ifnum\theExDepth=2\topsep .3\Extopsep\else\topsep 0pt\fi \parsep\z@\itemsep\z@} {\ifnum\theExDepth>1\topsep\csname pxtop\roman{ExDepth}\endcsname \itemsep\csname pxitem\roman{ExDepth}\endcsname\else\topsep\z@\itemsep\z@\fi \parsep\z@}{}{}

  \makeatother

\newlength{\pxtopii} \newlength{\pxtopiii} \newlength{\pxitemii} \newlength{\pxitemiii}

\newcommand{\spaceab}[1]{\setlength{\pxitemii}{#1}} \newcommand{\spaceii}[1]{\setlength{\pxitemiii}{#1}} \newcommand{\spacebefa}[1]{\setlength{\pxtopii}{#1}} \newcommand{\spacebefi}[1]{\setlength{\pxtopiii}{#1}}

\newcommand{\alignright}{\hspace{1em}\raggedright\hspace*{\fill}}

\NewDocumentCommand{\web}{o}{% \begingroup\alignright% (\textsc{web}\IfValueT{#1}{,~#1})\endgroup\par}

\begin{document}

\spaceab{1ex}
\spacebefa{2ex}

\ex. Example 1\label{ex1} \a. Sublevel1a\label{ex1a}\ \tslt{translation 1a} \b. Sublevel1b\label{ex1b}\ \tslt{translation 1b}

\spaceab{2ex} \spaceii{1ex}

\ex. Example 2\label{ex2}\ \a. sublevel2a \a. sublevel 2ai\ \tslt{translation 2ai} \b. sublevel 2aii \tslt{translation 2aii} \z. \b. \a. sublevel 2bi\ \tslt{translation 2bi} \b. sublevel 2bii\ \tslt{translation 2biii} \b. sublevel 2biii \tslt{translation 2biii} \z. \b. sublevel2c\ \tslt{translation 2c}

\ex. Example XXXXX \tslt{translation}\web\vspace*{1ex}

\end{document}

Depending on how heavy the sentences and translations are, and how the overall look feels, I adjust the spacings. The space before a translation is given with some \vspace*{} value. But the line break sometimes is already given by the previous command (such as \web), so i do not want to systematically integrate it.


  • (1) You can't add vertical space in horizontal mode, so the first version can't work. (Or it is not clear what you mean. Maybe you're in vertical mode already?) (2) An infinity of them isn't possible since you will eventually exceed TeX's maximum dimension, if nothing else. An indefinite number is a different matter. But what is the context for this? In most contexts, you shouldn't end a line with \\ or add vertical space without glue. – cfr Mar 03 '24 at 06:07
  • I'm not sure what you mean, oh you mean adding the second vspace we would be in hmode? But we would add it once at the same time of course. Glue, you mean plus / minus ? so that it can stretch ? There is no specific context, i always have to add manual \vspace according to how much space my examples and their translations take, and the commands are complicated so I won't automatize it. I don't use plus/minus. – Vincent Krebs Mar 03 '24 at 06:13
  • 1
    So are we between paragraphs? Then I don't get the line break toggle. But, really, if you're having to adjust the spacing manually that much, something is wrong. You should only need to do that kind of manual adjustment in corner cases and adding precise amounts of vertical space isn't usually the best solution, even then. – cfr Mar 03 '24 at 06:17
  • Let's say, the sentence is short, then I will write the translation just after, just breaking line. But if it's longer and takes more than one line of text, I'll add a \vspace*{0.5ex}. Then according to the compelxity of example nesting and that kind of factors, I adjust the values of the lengths between sublevels. This is all in linguex environments. – Vincent Krebs Mar 03 '24 at 06:20
  • 1
    I think an example would help, at least for non-linguists. Maybe somebody who uses linguex will visualise what you mean and be able to answer without one, but I think it would still be good to see what you're doing now. I suspect this is an XY problem, which makes it hard to help without some context. – cfr Mar 03 '24 at 06:25
  • 1
    you should probably be using addvspace not vspace so that it automatically adjusts to space added previously. but it's hard to guess with the current description of the problem – David Carlisle Mar 03 '24 at 08:33
  • I will try addvspace. And sorry if the description is incomplete, did my best. – Vincent Krebs Mar 03 '24 at 09:35
  • you have posted incomplete fragments so it's hard to see what you mean also I assume you mean \web\vspace*{1ex} not \web\\vspace*{1ex} but perhaps you mean \web\\ \vspace*{1ex} hard to tell when that fragment isn't runnable. – David Carlisle Mar 03 '24 at 09:49
  • @DavidCarlisle Sorry it was \web\vspace*{1ex} I corrected. But now after wipet's reminder, I know that even if I'm already in vmode, it is not a problem to add \par redundantly so I'll just remove the star from the command. I was misrepresenting things as if two line breaks would be bad. I think this is because of the \ command which returns "there is no line here to end" if in vmode already, but apparently \par does not behave the same way. – Vincent Krebs Mar 03 '24 at 09:56
  • @VincentKrebs yes \\ has to be in hmode otherwise it generates an error as it's a line break within a paragraph \par ends a paragraph if it's in one and does (almost) nothing if it is not. – David Carlisle Mar 03 '24 at 10:05
  • the \raggedright in that definition isn't doing anything (as you end the group before it is used) but again you only provide this in an untested fragment not in your test file. – David Carlisle Mar 03 '24 at 10:07
  • But the fragment is meant to be part of the MWE, it's just that i added it afterwards. \raggedright was in order for the [web] or [author] mention to align right without creating ridiculous inter-word spaces, which justification would do. It is meant to un-justify the paragraph preceding the command. – Vincent Krebs Mar 03 '24 at 10:15

4 Answers4

5

Because I don't understand why do you want to insert vertical spaces in horizontal mode (i.e. \spac without*), I show you only the principle of the unlimited + arguments. And, of course, the \par at the beginning of the macro means that we will insert the space in vertical mode only.

Or do you mean \vadjust usage? This is TeX primitive (used in LaTeX \vspace) which is a source of many confusions of LaTeX users, so I don't use it in the \spac macro.

\def\spac{\par \spacA+}
\def\spacA+{\vskip.5ex \futurelet\next\spacB}
\def\spacB{\ifx\next+\expandafter\spacA \fi}

% testing:

\hrule width1cm \spac \hrule \spac+ \hrule \spac++ \hrule \spac++++++ \hrule

\bye

wipet
  • 74,238
  • Because some of my commands already include a line break and I don't want to multiply the \. For example I say \web, it writes [web] (meaning that the sentence is from the web) and auto adds a line break to prepare the translation. If I want to use vspace, i won't use another line break in addition. So I write \web\vspace*{0.5ex} – Vincent Krebs Mar 03 '24 at 06:44
  • Sorry, do you mean to use \par as a simple check? If it's only a check and two \par won't cumulate (no idea), then ok. But they must not cumulate, because if there is already a \par before, i.e. if we are after the \web command for example, I do NOT want to have two breaks. I'd better use \ifvmode and return an error message if \vmodefalse. – Vincent Krebs Mar 03 '24 at 07:57
  • TeX switches between vertical mode and horizontal mode. If there is a first letter in vertical mode, then TeX switch to the horizontal mode and prepares a paragraph as a single horizontal list. The paragraph is finalized by empty line in the source which is transformed to the \par command. The horizontal list is broken to the lines of the paragraph and they are put to the vertical list. \par in vertical mode does nothing. It means that \par\par is the same as \par. The switching between horizontal/vertical mode is basic principle of TeX, it should be known by all TeX users. – wipet Mar 03 '24 at 08:32
  • Ok, in this case I do not need to worry about redundancy. Thank you for this explanation of a basic principle. – Vincent Krebs Mar 03 '24 at 09:33
  • For vertical/horizontal mode, see https://tex.stackexchange.com/questions/13177/what-are-vertical-and-horizontal-modes – Vincent Krebs Mar 05 '24 at 04:34
2

The following logs

adding 2.15277pt
adding 4.30554pt
adding 75.34698pt

and produces

enter image description here

I didn't use the * form at all as you need to be in vertical mode to add vertical space.

\documentclass{article}
\newlength\spaclen
\NewDocumentCommand\spac{}{\par\setlength\spaclen{0.5ex}\innerspac}
\NewDocumentCommand\innerspac{t{+}}{%
  \IfBooleanTF{#1}%
  {\addtolength\spaclen{.5ex}\innerspac}%
  {\typeout{adding \the\spaclen}\addvspace\spaclen}}

\begin{document}

aaa\spac b

aaa\spac+ b

aaa\spac++++++++++++++++++++++++++++++++++ b \end{document}

David Carlisle
  • 757,742
  • Thank you, so are both solutions equivalent ? – Vincent Krebs Mar 03 '24 at 10:26
  • 1
    @VincentKrebs you mean mine and wipets? They are similar but mine is using latex constructs (and skips over space so \spac + + + is three +, wipet (as ever) avoids latex constructs and uses tex primitives (and doesn't skip over space) – David Carlisle Mar 03 '24 at 10:29
  • wipet likes low-level I guess :) – Vincent Krebs Mar 03 '24 at 10:30
  • t{+} and t+ are equivalent I guess. As an exercice I'll add a symmetrical "minus" boolean to add negative 0.5 increments. – Vincent Krebs Mar 03 '24 at 10:35
  • 1
    @VincentKrebs It's good (sometimes) to see how things work below the surface but in general it's not advisable to actually use tex primitives in a latex document as you can easily cut through latex conventions and break things. redefining the standard latex command \value for example will have unexpected consequences (eg it breaks the standard bibliography commands) – David Carlisle Mar 03 '24 at 10:35
  • 2
    I would definitely use {+} here as + works but + is already a syntax command in the \NewDocumentCommand argument syntax +m is an argument allowing \par so the fact that t+m parses as t{+} m not t +m is true but relying on low level details of the argument parser. @VincentKrebs – David Carlisle Mar 03 '24 at 10:37
  • Yes there is that + too, all right. – Vincent Krebs Mar 03 '24 at 10:38
  • Yeah it's powerful, you have to know exactly what you're doing. – Vincent Krebs Mar 03 '24 at 10:39
  • You made a loop... – Vincent Krebs Mar 03 '24 at 11:10
  • @VincentKrebs ? – David Carlisle Mar 03 '24 at 11:11
  • you made it recurrent somehow – Vincent Krebs Mar 03 '24 at 11:11
  • 1
    @VincentKrebs oh yes of course (same as wipet) here \innerspac looks for one + and calls itself so it keeps getting called until there is no + when a space gets added. – David Carlisle Mar 03 '24 at 11:12
  • 1
    @VincentKrebs \spac just initializes the loop by setting the length to 0 and calling the inner command. – David Carlisle Mar 03 '24 at 11:14
  • 1
    @VincentKrebs wipet doesn't use LaTeX, so there's no danger of breaking LaTeX commands. – cfr Mar 03 '24 at 21:34
  • David, I realize, looking at my document and your log... so your \spac (with no +) adds 0pt? What's the use? It was supposed to add 0.5ex :)) – Vincent Krebs Mar 05 '24 at 04:21
  • 1
    @VincentKrebs oh sorry must have misread your question, you can of course change the initialisation, I'll update later – David Carlisle Mar 05 '24 at 07:13
  • \setlength\spaclen{.5ex} right? – Vincent Krebs Mar 05 '24 at 07:27
  • @VincentKrebs no. it will just start at .5 and add .5 each time, I'll update the code above – David Carlisle Mar 05 '24 at 08:56
  • @VincentKrebs updated answer (I rejected your edit so I could update all including log and output image) the code edit was the same. – David Carlisle Mar 05 '24 at 09:01
  • Thanks. My logs slightly vary from yours:
    • adding 2.155pt
    • adding 4.31pt
    • adding 75.42496pt
    – Vincent Krebs Mar 05 '24 at 09:14
  • 1
    @VincentKrebs that log is not implementation specific and the metrics for cmr haven't changed since 1987 or thereabouts. Check your code is exactly above, the document as above produces adding 2.15277pt adding 4.30554pt adding 75.34698pt ooh no I see you are using lualatex (or xelatex) so are using latin modern not computer modern so yes you will get adding 2.155pt adding 4.31pt adding 75.42496pt – David Carlisle Mar 05 '24 at 09:41
2

Count the number of + following \spac or \spac*.

I achieve this by doing a regex search and replace. A space following the last + or the single * is ignored.

\documentclass{article}
\usepackage{indentfirst}% just for alignment after \subsubsection

\ExplSyntaxOn

\NewDocumentCommand\spac{s} { \IfBooleanTF { #1 } { \peek_regex_replace_once:nn { (+)\s } { \c{krebs_spac_break:n}{\1} } } { \peek_regex_replace_once:nn { (+)\s } { \c{krebs_spac_nobreak:n}{\1} } } }

\cs_new_protected:Nn \krebs_spac_break:n { \typeout{Adding~\inteval{\tl_count:n{#1}+1}ex} \[\inteval{\tl_count:n{#1}+1}ex] } \cs_new_protected:Nn \krebs_spac_nobreak:n { \typeout{Adding~\inteval{\tl_count:n{#1}+1}ex} \vspace{\inteval{\tl_count:n{#1}+1}ex} }

\ExplSyntaxOff

\begin{document}

\subsubsection*{zero} aaa\spac b

aaa\spac* b

\subsubsection*{one} aaa\spac+ b

aaa\spac*+ b

\subsubsection*{two} aaa\spac++ b

aaa\spac*++ b

\subsubsection*{three} aaa\spac+++ b

aaa\spac*+++ b

\end{document}

enter image description here

Console output (the \typeout instructions are just for this)

Adding 1ex
Adding 1ex
Adding 2ex
Adding 2ex
Adding 3ex
Adding 3ex
Adding 4ex
Adding 4ex
egreg
  • 1,121,712
0

David Carlisle's command \innerspac can be slightly modified in order to support the minus sign -.

All we have to do is check for a - sign after checking for a +. As long as one of the two symbols is found, the loop goes on. It stops when both boolean values are False.

% !TEX TS-program = lualatex

\documentclass{article}

\newlength\spaclen

\NewDocumentCommand\spac{}{\par\setlength\spaclen{.5ex}\innerspac}

\NewDocumentCommand\innerspac{t{+}t{-}}{% \IfBooleanTF{#1}{% \addtolength\spaclen{.5ex}% \IfBooleanTF{#2}{% \addtolength\spaclen{-.5ex}}{}% \innerspac}{% \IfBooleanTF{#2}{% \addtolength\spaclen{-.5ex}\innerspac}{% \typeout{adding \the\spaclen}\addvspace\spaclen}}}

\begin{document}

aaa\spac b

aaa\spac+ b

aaa\spac- b

aaa\spac-- b

aaa\spac--- b

aaa\spac++++++++++++++++++++++++++++++++++ b

aaa\spac++--- b

aaa\spac+-+-+-+- b

\end{document}

Logs (with LuaTeX engine):

  • adding 2.155pt
  • adding 4.31pt
  • adding 0.0pt
  • adding -2.155pt
  • adding -4.31pt
  • adding 75.42496pt
  • adding 0.0pt
  • adding 2.155pt

enter image description here