0

I'm working on an environment in which each sentence is supposed to end with a line break. This should happen automatically and with minimal intrusion into the text because in the actual use case the contents of the environment (which will be \included from another file) will be reused in other documents where this behavior is undesirable. In another question it was proposed that this could be accomplished by making the full stops active so that they functioned as a command which would not only print the full stop normally but also insert the desired line break. When combined with a command to protect non-full stop uses of the character (which is intrusive, but not too bad), this works reasonably well for cases where the full stop ends the sentence but runs afoul when closing delimiters (quotation marks, parentheses, braces, and brackets) come into play.

Such closing delimiters could, in theory, be added to the list of active characters within the environment, but doing so would make for a much longer list of active characters and require a much more intrusive use of the protection function.

Is there a way to adapt the answer (a version of which is shown below) which would take closing delimiters into account while not requiring a huge amount of intrusion into the text?

\documentclass{article}

\begingroup
  \catcode`\.=\active
  \catcode`\?=\active
  \catcode`\!=\active
  \gdef.{\normalperiod\formattingcommand}%
  \gdef?{\normalqmark\formattingcommand}%
  \gdef!{\normalexclam\formattingcommand}%
\endgroup
\newcommand\formattingcommand{\newline\makebox[0pt][r]{$\rightarrow$}\ignorespaces}
\newenvironment{speech}
  { \vspace{2ex}\par
    \let\normalperiod=.
    \let\normalqmark=?
    \let\normalexclam=!
    \catcode`\.=\active
    \catcode`\?=\active
    \catcode`\!=\active
    \sffamily\small
  }{\vspace{2ex}\par}

\chardef\periodcatcode=\catcode`\.
\chardef\qmarkcatcode=\catcode`\?
\chardef\exclamcatcode=\catcode`\!
\newcommand\aeprotect[1]{%%
  \begingroup
  \catcode`\.=\periodcatcode
  \catcode`\?=\qmarkcatcode
  \catcode`\!=\exclamcatcode
  \scantokens{#1\ignorespaces}%%
  \endgroup
  }

\begin{document}

GALLIA est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur. Hi omnes lingua,
institutis, legibus inter se differunt.  Gallos ab Aquitanis Garumna flumen, a Belgis Matrona et Sequana dividit.  Horum omnium fortissimi sunt Belgae, propterea quod a
cultu atque humanitate provinciae longissime absunt, minimeque ad eos mercatores saepe commeant atque ea quae ad effeminandos animos pertinent important, proximique sunt
\begin{speech}
\providecommand{\aeprotect}{\relax}
This is the beginning of paragraph 1.
Lorem ipsum dolor sit amet, consectetur adipiscing elit?
\aeprotect{S.P.Q.R.} Sed faucibus, sem vel suscipit eleifend, ipsum dolor tempus nunc, ut iaculis nibh arcu vitae tortor.
Pellentesque a efficitur lectus, eget sagittis lorem.
Fusce vestibulum feugiat nibh, pulvinar convallis eros sagittis ac.
Pellentesque aliquam arcu a augue malesuada, sit amet eleifend orci vulputate!
Sed nibh dolor, commodo vel risus quis, elementum consequat dui.

This is the beginning of paragraph 2.
Pellentesque eu neque eleifend, tincidunt mauris id, ultricies odio.
Aenean ac nisi congue, mollis leo a, cursus dolor!
Aenean non justo felis.
Cras interdum quam eu metus imperdiet ultricies.
Fusce finibus pellentesque volutpat?
“Praesent id lacus eget dui ullamcorper finibus nec nec urna?”
“Quisque vel vestibulum quam.”
(Lorem ipsum dolor sit amet, consectetur adipiscing elit.)
Mauris vel nisl eros.

\end{speech}
Germanis, qui trans Rhenum incolunt, quibuscum continenter bellum gerunt. Qua de causa Helvetii quoque reliquos Gallos virtute praecedunt, quod fere cotidianis proeliis
cum Germanis contendunt, cum aut suis finibus eos prohibent aut ipsi in eorum finibus bellum gerunt.

\end{document}

Oh, and in case it is useful, I use LuaLaTeX as my default engine, so feel free to employ features of that engine, if needed.

  • This isn’t about honoring input line breaks. The example is structured that way because of an alternative solution which I was investigating. Normally I wouldn’t have them there. – rpspringuel Sep 28 '18 at 19:41
  • Does my answer help you? If so it'd be great if you showed some approval. If not: What is still bothering you? – Skillmon likes topanswers.xyz Oct 1 '18 at 19:33
  • @Skillmon Just have a lot on my plate at the moment and haven’t had the time to get back to this yet. I hope to look at it tomorrow. – rpspringuel Oct 1 '18 at 19:42
0

The following gives you a definable exception list \formattingcommand@exceptionlist in which you can define tokens which should be treated special after your full stops. Those exceptions should be single characters which should be output before your \newline.... Note that this only works for Unicode enabled engines (so XeTeX and LuaLaTeX), others will throw an error (because of the ending quote character you use).

\documentclass{article}

\begingroup
  \catcode`\.=\active
  \catcode`\?=\active
  \catcode`\!=\active
  \gdef.{\normalperiod\formattingcommand}%
  \gdef?{\normalqmark\formattingcommand}%
  \gdef!{\normalexclam\formattingcommand}%
\endgroup
\makeatletter
\newcommand\formattingcommand@exceptionlist{(,),”}
\newif\if@formattingcommand@bool@
\newcommand\formattingcommand{\futurelet\next\formattingcommand@A}
\newcommand\formattingcommand@A
  {%
    \@formattingcommand@bool@false
    \@for\zz:=\formattingcommand@exceptionlist\do
      {%
        \expandafter\let\expandafter\zzz\zz
        \unskip % remove a trailing space from \zz
        \ifx\zzz\next
          \@formattingcommand@bool@true
        \fi
      }%
    \if@formattingcommand@bool@
      \expandafter\formattingcommand@C
    \else
      \formattingcommand@B
    \fi
  }
\newcommand\formattingcommand@B
  {\newline\makebox[0pt][r]{$\rightarrow$}\ignorespaces}
\newcommand\formattingcommand@C[1]
  {#1\formattingcommand@B}
\makeatother
\newenvironment{speech}
  { \vspace{2ex}\par
    \let\normalperiod=.
    \let\normalqmark=?
    \let\normalexclam=!
    \catcode`\.=\active
    \catcode`\?=\active
    \catcode`\!=\active
    \sffamily\small
    \ignorespaces
  }{\vspace{2ex}\par}

\chardef\periodcatcode=\catcode`\.
\chardef\qmarkcatcode=\catcode`\?
\chardef\exclamcatcode=\catcode`\!
\newcommand\aeprotect[1]{%%
  \begingroup
  \catcode`\.=\periodcatcode
  \catcode`\?=\qmarkcatcode
  \catcode`\!=\exclamcatcode
  \scantokens{#1\ignorespaces}%%
  \endgroup
  }

\begin{document}

GALLIA est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur. Hi omnes lingua,
institutis, legibus inter se differunt.  Gallos ab Aquitanis Garumna flumen, a Belgis Matrona et Sequana dividit.  Horum omnium fortissimi sunt Belgae, propterea quod a
cultu atque humanitate provinciae longissime absunt, minimeque ad eos mercatores saepe commeant atque ea quae ad effeminandos animos pertinent important, proximique sunt
\begin{speech}
\providecommand{\aeprotect}{\relax}
This is the beginning of paragraph 1.
Lorem ipsum dolor sit amet, consectetur adipiscing elit?
\aeprotect{S.P.Q.R.} Sed faucibus, sem vel suscipit eleifend, ipsum dolor tempus nunc, ut iaculis nibh arcu vitae tortor.
Pellentesque a efficitur lectus, eget sagittis lorem.
Fusce vestibulum feugiat nibh, pulvinar convallis eros sagittis ac.
Pellentesque aliquam arcu a augue malesuada, sit amet eleifend orci vulputate!
Sed nibh dolor, commodo vel risus quis, elementum consequat dui.

This is the beginning of paragraph 2.
Pellentesque eu neque eleifend, tincidunt mauris id, ultricies odio.
Aenean ac nisi congue, mollis leo a, cursus dolor!
Aenean non justo felis.
Cras interdum quam eu metus imperdiet ultricies.
Fusce finibus pellentesque volutpat?
“Praesent id lacus eget dui ullamcorper finibus nec nec urna?”
“Quisque vel vestibulum quam.”
(Lorem ipsum dolor sit amet, consectetur adipiscing elit.)
Mauris vel nisl eros.

\end{speech}
Germanis, qui trans Rhenum incolunt, quibuscum continenter bellum gerunt. Qua de causa Helvetii quoque reliquos Gallos virtute praecedunt, quod fere cotidianis proeliis
cum Germanis contendunt, cum aut suis finibus eos prohibent aut ipsi in eorum finibus bellum gerunt.

\end{document}

enter image description here

Since the above changes the definition of . you can't use . in an \input statement inside of the speech environment, because you'd end up trying to \input something like foo\normalperiod\formattingcommand tex which can't work out. The trick is to utilize the fact that category codes don't change in TeX once a token got read in. The following therefore works for filenames containing a period:

\newcommand\speechinput[1]
  {%
    \begin{speech}
      \providecommand\aeprotect{\relax}%
      \input{#1}%
    \end{speech}%
  }

The dot gets tokenized when you specify the argument of \speechinput and therefore is not substituted with \normalperiod\formattingcommand in the inner \input statement, which is inside of the speech environment, so any dots inside the inputted file get evaluated as intended. Complete example:

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents}{\jobname-input.tex}
This is the beginning of paragraph 1.
Lorem ipsum dolor sit amet, consectetur adipiscing elit?
\aeprotect{S.P.Q.R.} Sed faucibus, sem vel suscipit eleifend, ipsum dolor tempus nunc, ut iaculis nibh arcu vitae tortor.
Pellentesque a efficitur lectus, eget sagittis lorem.
Fusce vestibulum feugiat nibh, pulvinar convallis eros sagittis ac.
Pellentesque aliquam arcu a augue malesuada, sit amet eleifend orci vulputate!
Sed nibh dolor, commodo vel risus quis, elementum consequat dui.

This is the beginning of paragraph 2.
Pellentesque eu neque eleifend, tincidunt mauris id, ultricies odio.
Aenean ac nisi congue, mollis leo a, cursus dolor!
Aenean non justo felis.
Cras interdum quam eu metus imperdiet ultricies.
Fusce finibus pellentesque volutpat?
“Praesent id lacus eget dui ullamcorper finibus nec nec urna?”
“Quisque vel vestibulum quam.”
(Lorem ipsum dolor sit amet, consectetur adipiscing elit.)
Mauris vel nisl eros.
\end{filecontents}

\begingroup
  \catcode`\.=\active
  \catcode`\?=\active
  \catcode`\!=\active
  \gdef.{\normalperiod\formattingcommand}%
  \gdef?{\normalqmark\formattingcommand}%
  \gdef!{\normalexclam\formattingcommand}%
\endgroup
\makeatletter
\newcommand\formattingcommand@exceptionlist{(,),”}
\newif\if@formattingcommand@bool@
\newcommand\formattingcommand{\futurelet\next\formattingcommand@A}
\newcommand\formattingcommand@A
  {%
    \@formattingcommand@bool@false
    \@for\zz:=\formattingcommand@exceptionlist\do
      {%
        \expandafter\let\expandafter\zzz\zz
        \unskip % remove a trailing space from \zz
        \ifx\zzz\next
          \@formattingcommand@bool@true
        \fi
      }%
    \if@formattingcommand@bool@
      \expandafter\formattingcommand@C
    \else
      \formattingcommand@B
    \fi
  }
\newcommand\formattingcommand@B
  {\newline\makebox[0pt][r]{$\rightarrow$}\ignorespaces}
\newcommand\formattingcommand@C[1]
  {#1\formattingcommand@B}
\makeatother
\newenvironment{speech}
  { \vspace{2ex}\par
    \let\normalperiod=.
    \let\normalqmark=?
    \let\normalexclam=!
    \catcode`\.=\active
    \catcode`\?=\active
    \catcode`\!=\active
    \sffamily\small
    \ignorespaces
  }{\vspace{2ex}\par}

\chardef\periodcatcode=\catcode`\.
\chardef\qmarkcatcode=\catcode`\?
\chardef\exclamcatcode=\catcode`\!
\newcommand\aeprotect[1]{%%
  \begingroup
  \catcode`\.=\periodcatcode
  \catcode`\?=\qmarkcatcode
  \catcode`\!=\exclamcatcode
  \scantokens{#1\ignorespaces}%%
  \endgroup
  }

\newcommand\speechinput[1]
  {%
    \begin{speech}
      \providecommand\aeprotect{\relax}%
      \input{#1}%
    \end{speech}%
  }

\begin{document}

GALLIA est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur. Hi omnes lingua,
institutis, legibus inter se differunt.  Gallos ab Aquitanis Garumna flumen, a Belgis Matrona et Sequana dividit.  Horum omnium fortissimi sunt Belgae, propterea quod a
cultu atque humanitate provinciae longissime absunt, minimeque ad eos mercatores saepe commeant atque ea quae ad effeminandos animos pertinent important, proximique sunt

\speechinput{\jobname-input.tex}
Germanis, qui trans Rhenum incolunt, quibuscum continenter bellum gerunt. Qua de causa Helvetii quoque reliquos Gallos virtute praecedunt, quod fere cotidianis proeliis
cum Germanis contendunt, cum aut suis finibus eos prohibent aut ipsi in eorum finibus bellum gerunt.

\end{document}

Output is obviously the same as the above.

  • This works just fine for me in a sample like the MWE I posted, but is failing in the actual use case. In my actual use case the contents of the speech environment are off-loaded into text.tex and inserted with a \input{text.tex} statement. When that is done, compilation fails, complaining about \unskip being used in vertical mode. Of course the MWE I posted also fails under these conditions, just for a different reason. Sorry I didn't notice that before. – rpspringuel Oct 3 '18 at 1:03
  • So, it turns out that leaving off the extension allows \input to work. I don't like doing that since it's not my normal way of doing things, but it is something that I can work with if necessary. – rpspringuel Oct 3 '18 at 1:32
  • @rpspringuel I'll post an edit dealing with inputting of files. – Skillmon likes topanswers.xyz Oct 3 '18 at 7:28

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.