2

I'm using code from The TeXBook to align some text to the right of the last line of the paragraph or to the of the next line if there isn't room on the last line of the paragraph. (See chapter 14, page 106 and implementation below.)

It works well, except there are situations when I have a section of text in an environment using \obeylines. This messes things up, since \obeylines inserts a paragraph break at the end of each line including the last line and the text to be aligned to the right always appears on a line by itself even if there is space for it on the previous line.

I can work around this by suppressing the paragraph break on the last line of the \obeylines section with a % on the end of the line.

But I'm after an automatic way that doesn't require me to do this.

Here's a MWE to play with.

The zzsection and olsection environments simulate environments I use in my document structure. I'd like to just modify code in these two environments, not the text within the environments. The olsection may occur with ordinary text above or below it. I.e., the only case that needs to be worked around is when a zzsection finishes with an olsection environment. Filler text is of arbitrary length and content. Hopefully that makes sense…

In this example, the third paragraph is the one that needs correcting.

\documentclass{article}

\usepackage{lipsum}

\newcommand{\zz}{% {\unskip\nobreak\hfil\penalty50 \hskip2em\hbox{}\nobreak\hfil\textbf{Filler text}% \parfillskip=0pt \finalhyphendemerits=0 \par}}

\newenvironment{zzsection}{}{\zz} \newenvironment{olsection}{\obeylines}{}

\begin{document}

\begin{zzsection} \lipsum[1] \end{zzsection}

\begin{zzsection} \lipsum[5] \end{zzsection}

\begin{zzsection} \begin{olsection} \lipsum[1] \end{olsection} \end{zzsection}

\begin{zzsection} \begin{olsection} \lipsum[5] \end{olsection} \end{zzsection}

\end{document}


Update

Henri Menke's code is almost perfect for me.

My actual document has a \leftskip 1cm inside the olsection environment. This isn't taken into account for the last line with his answer. i.e., this fails:

\begin{zzsection}
  \begin{olsection}
    \leftskip 1cm
    Some text.
    Some text.
    Some text.
  \end{olsection}
\end{zzsection}
David Purton
  • 25,884

1 Answers1

3

Just make obeylines look ahead for \end.

If there may be multiple blank lines before the \end you might need to skip these recursively. This is quite slow because it absorbs tokens one by one.

\documentclass{article}

\usepackage{lipsum}

\newcommand{\zz}{% {\unskip\nobreak\hfil\penalty50 \hskip2em\hbox{}\nobreak\hfil\textbf{Filler text}% \parfillskip=0pt \finalhyphendemerits=0 \par}}

% Loosely adapted from ConTeXt % % Two stage indirection via \obeylineshandler is such that we can change % \obeyedline at some later stage. \newcommand\obeyedline{\par} \newcommand\obeylineshandler{\obeyedline} {\catcode\^^M=\active % these lines must end with % \protected\gdef\obeylines{\catcode^^M\active \let^^M\obeylineshandler}% \global\let^^M\obeylineshandler} % this is in case ^^M appears in a \write

% Look ahead for \end and skip any ^^M on the way there \makeatletter {\catcode`^^M=\active % these lines must end with % \gdef\obeylineend{% @ifnextchar\end{}{% @ifnextchar^^M{% \expandafter\obeylineend@gobble}{\par}}}} \makeatother

\newenvironment{zzsection}{}{\zz} \newenvironment{olsection}{\let\obeyedline\obeylineend\obeylines}{}

\begin{document}

\begin{zzsection} \lipsum[1] \end{zzsection}

\begin{zzsection} \lipsum[5] \end{zzsection}

\begin{zzsection} \begin{olsection} \lipsum[1]

\end{olsection} \end{zzsection}

\begin{zzsection} \begin{olsection} \lipsum[5] \end{olsection} \end{zzsection}

\end{document}

Henri Menke
  • 109,596
  • Ooh! This looks very promising. It doesn't quite work in my actual document, but I may be able to fix that. – David Purton Jul 10 '20 at 02:48
  • @DavidPurton I can guess your problem is with multiple blank lines before the \end. See my updated answer for that. – Henri Menke Jul 10 '20 at 02:59
  • I have a \leftskip 1cm in my olsection environment. This seems to be ignored on the last line. – David Purton Jul 10 '20 at 03:00
  • Speed isn't a concern. These documents are short and this structure only appears rarely. – David Purton Jul 10 '20 at 03:03
  • Your initial \obeylineend macro gives better results in my actual code. i may need to ask a new question with a little more detail of what actually is in the olsection environment. (It's quite similar to the psalm environment at https://tex.stackexchange.com/a/360322/87678) – David Purton Jul 10 '20 at 03:09
  • Regarding the \leftskip, you have to move it outside of the olsection environment. The \leftskip is only applied when \par is encountered but that is outside of the group defined by olsection for the last line of text. Maybe you could smuggle the \leftskip out using some \aftergroup trickery but that might have other unexpected side-effects and is of course incomplete, because the same situation would apply to other paragraph parameters like \rightskip or \baselineskip, etc. – Henri Menke Jul 14 '20 at 00:11
  • I don't see how it makes a difference putting it outside the olsection? The right aligned text is attached to the zzsection, so if there's no extra text after the olsection, the indent is returned to 0pt and is still wrong. I have now got a working implementation involving an \everypar{}, a boolean check, and an \aftergroup, but it's not pretty. It requires a blank line after the olsection, so if I don't want a new paragraph I need to insert \noindent. I can live with that. I might still follow up with another question. (Still only your original \futurelet code works.) – David Purton Jul 14 '20 at 00:56
  • @DavidPurton I don't know exactly what's going on in your case, but my previous comment was about a setup like this: http://dpaste.com/0190C27 (expires in 10 days) – Henri Menke Jul 14 '20 at 02:04
  • Yes, but that's no good, because the \leftskip has to return to 0pt after the olsection since sometimes (often actually) there is more text between \end{olsection} and the \end{zzsection}. Don't worry for now. I'll think about a better defined follow-up question. Already what you've done has helped. – David Purton Jul 14 '20 at 02:41