2

I have defined a command, that is used in text scripts for stageplays.

At the moment, you can write:

\says[quietly]{Minka}{Hello, are you there?}

which compiles to something like:

*Minka*:     (quietly) Hello, are you there?

where Minka is bold and quietly is slanted and colored.

however, I was asked, if i could provide "multiple square brackets" as they wanted to write:

\says{Minka}{[quietly]Hello, are you there? [shouting] Is anyone there?}

which should compile to:

*Minka*:     (quieltly) Hello, are you there? (shouting) Is anyone there?

My question is, can I somehow parse the text that is given to \says{name}{text} and replace all parts between square brackets with some command?


EDIT: Current Says Command:

\newlength\widest
\settowidth\widest{\textbf{Longest Name in document}}
\newcommand{\says}[2]{
\begin{description}[leftmargin=\dimexpr\widest+\labelsep\relax,labelindent=0pt,labelwidth=\widest]
    \item[#1] #2
\end{description}
}

where the #2 should be the part on the right. I also redefine the command at the beginning of the script:

\newcommand\minka[1]{\says{Minka}{#1}} \newcommand\arthur[1]{\says{Arthur}{#1}}

and probably use more than one line or paragraph of text:

\minka{Hello, you//
[louder] yes, you there!

[normal voice] Is it you that helped me with that latex stuff?
}


3
  • It is possible, but an input syntax like \says{Minka}{\how{quietly} Hello, are you there? \how{shouting} Is anyone there?} as @user605895 seems to be suggesting looks like cleaner markup to me.
    – frougon
    Jun 15, 2019 at 11:32
  • Yes, but sqare brackets arebway easier to explain to our users.
    – Tobi
    Jun 15, 2019 at 17:20
  • Okay, please see my answer below.
    – frougon
    Jun 15, 2019 at 19:01

3 Answers 3

6

What you asked for can be done with LaTeX3's regex module:

\documentclass{article}
\usepackage{xcolor}
\usepackage{xparse}

\definecolor{saysHow}{RGB}{37,29,118}

\ExplSyntaxOn

\regex_const:Nn \l__tobiscript_howsays_regex { \[ ([^\]]+) \] }

\cs_new_protected:Npn \tobiscript_says:nn #1#2
  {
    \tl_set:Nn \l_tmpa_tl {#2}
    \regex_replace_all:NnN \l__tobiscript_howsays_regex
      { \c{how} \cB\{ \1 \cE\} }
      \l_tmpa_tl

    \textbf{#1:} \nobreakspace \tl_use:N \l_tmpa_tl
    \par
  }

\NewDocumentCommand \says { m m }
  {
    \tobiscript_says:nn {#1} {#2}
  }

\ExplSyntaxOff

% Define how to format the things inside brackets
\newcommand*{\how}[1]{%
  \textcolor{saysHow}{\textsl{(#1)}}%
  ~\ignorespaces             % replace following spaces with one nobreak space
}

\setlength{\parindent}{0pt}

\begin{document}

\says{Minka}{[quietly]Hello, are you there? [shouting] Is anyone there?}
\says{Arthur}{[hesitating] Uh, maybe me?.. [now assured] Patsy!
  Patsy's here!}

\end{document}

Screenshot

Regarding your edited question, you can use this. In case you are not interested in the old \says command anymore, just remove its definition, then rename \newsays to \says or whatever name you want for the new commmand.

\documentclass{article}
\usepackage{xcolor}
\usepackage{enumitem}
\usepackage{xparse}

\definecolor{saysHow}{RGB}{37,29,118}

\ExplSyntaxOn

\regex_const:Nn \l__tobiscript_howsays_regex { \[ ([^\]]+) \] }

\cs_new_protected:Npn \tobiscript_format_howsays_in_text:n #1
  {
    \tl_set:Nn \l_tmpa_tl {#1}
    \regex_replace_all:NnN \l__tobiscript_howsays_regex
      { \c{how} \cB\{ \1 \cE\} }
      \l_tmpa_tl

    \tl_use:N \l_tmpa_tl
  }

\cs_new_protected:Npn \tobiscript_says:nn #1#2
  {
    \textbf{#1:} \nobreakspace \tobiscript_format_howsays_in_text:n {#2}
    \par
  }

% \says command as requested in the original question
\NewDocumentCommand \says { m +m }
  {
    \tobiscript_says:nn {#1} {#2}
  }

\NewDocumentCommand \formathowsaysintext { +m }
  {
    \tobiscript_format_howsays_in_text:n {#1}
  }

\ExplSyntaxOff

\newlength{\widest}
\settowidth\widest{\textbf{Longest Name in document}}

% \newsays: command added to the question after the answer was provided
\newcommand{\newsays}[2]{%
  \begin{description}[leftmargin=\dimexpr\widest+\labelsep\relax,
                      labelindent=0pt,labelwidth=\widest]
    \item[#1] \formathowsaysintext{\ignorespaces #2\unskip}
  \end{description}%
}

% Define how to format the things inside brackets
\newcommand*{\how}[1]{%
  \textcolor{saysHow}{\textsl{(#1)}}%
  ~\ignorespaces             % replace following spaces with one nobreak space
}

\setlength{\parindent}{0pt}

\begin{document}

\says{Minka}{[quietly]Hello, are you there? [shouting] Is anyone there?}
\says{Arthur}{[hesitating] Uh, maybe me?.. [now assured] Patsy!
  Patsy's here!}

\newsays{Roger}{
  [irritated]Oh, what sad times are these when passing ruffians can say ``ni''
  at will to old ladies.

  [sorry for interrupting the show] Stupid paragraph added only to show this
  can be done. [again, really sorry!] Back to the text in one second.

  There is a pestilence upon this land. Nothing is sacred. Even
  those who arrange and design shrubberies are under considerable economic
  stress at this period in history.
}

\newsays{Arthur}{
  [showing sudden interest] Did you say ``shrubberies''?
 }
\end{document}

Screenshot

2
  • what do I need to do, in order to make it work as a subcommand for my already existing \says command? (see edit), what do I need to do to make it work for multiple lines of text?
    – Tobi
    Jun 18, 2019 at 19:25
  • Please see my last edit.
    – frougon
    Jun 19, 2019 at 6:43
3

You could use a different macro to indicate 'how' someone is saying something, e.g. as follows:

\documentclass{article}
\usepackage{xcolor}
\newcommand{\how}[1]{
    \textit{\textcolor{red}{(#1)}}
}
\begin{document}
\how{quietly} Hello, are you there? \how{shouting} Is anyone there? 
\end{document} 
1
  • I know how to define macros. The question was about square brackets
    – Tobi
    Jun 15, 2019 at 17:22
0

If you really want this interface, you can parse through the text with TeX:

\documentclass{article}

\usepackage{enumitem}

\newlength\widest
\settowidth\widest{\textbf{Longest Name in document}}

\makeatletter
    \def\@stop{\@stop}%
    \long\def\says@parse#1[#2]{%
        #1%
        \ifx\@stop#2\else
            \how{#2}%
            \expandafter\says@parse
        \fi
    }
    \long\def\says#1#2{%
        \begin{description}[leftmargin=\dimexpr\widest+\labelsep\relax,labelindent=0pt,labelwidth=\widest]%
            \item[#1]%
            \says@parse #2[\@stop]%
        \end{description}%
    }
    \def\how#1{%
        \ifhmode\unskip\ \fi
        \textsl{(#1)}%
        ~\ignorespaces
    }
    \let\normalbackslash\@backslashchar
\makeatother

\begin{document}

\says{Minka}{[quietly]Hello, are you there? [shouting] Is anyone there?}
\says{Tobi}{
    [annoyed]  Oh, don't be so fearful.
    You know I'm here!
    Just don't concentrate on what you're doing and you'll be fine.

    [rambling]
    It's not like a lot could go wrong anyway.
    I mean, it's just a program, your computer won't literally explode.
    If you somehow mess it up completely, we'll just reinstall it and you can try again.
    [resigned] But okay, I'll explain it to you again.

    [lecturing] You just write \texttt{\normalbackslash says}, then a pair of braces containing the name of the person speaking\dots
    [pausing] Good, like that.
    Now you open another pair of braces and put their dialogue in there.
    Close the brace, and [happily] you're done.
}

\end{document}

MWE output

This has some downsides.

  • The syntax is not very well suited to the use, I think. Whenever you input many paragraphs as an argument to a macro, stop and ask yourself if this really should be an environment
  • More importantly, as a consequence of this, you can't use any macros that change \catcodes inside of the text (like \verb). In my example, I had to use \texttt and \@backslashchar instead.

A better approach, in my opinion, would be the use of an environment and an active character. We do not want to make [ (or ]) active, because that would break all optional arguments. If you are open to changing the syntax from [instruction] to |instruction|, we can do this quite nicely though.

\documentclass{article}

\usepackage{enumitem}

\newlength\widest
\settowidth\widest{\textbf{Longest Name in document}}

\makeatletter
    \def\@speech#1{%
        \item[#1]%
        \catcode`\|=13
    }
    \newenvironment{dialogue}{%
        \let\speech\@speech
        \begin{description}[leftmargin=\dimexpr\widest+\labelsep\relax,labelindent=0pt,labelwidth=\widest]%
    }{%
        \end{description}%
    }
    \def\how#1{%
        \ifhmode\unskip\ \fi
        \textsl{(#1)}%
        ~\ignorespaces
    }
    {
        \catcode`\|=12
        \gdef\verticalbar{|}
        \catcode`\|=13
        \AtBeginDocument{%
            \gdef|{\ifmmode\verticalbar\else\expandafter\@how\fi}%
        }
        \gdef\@how#1|{%
            \how{#1}%
        }
    }
\makeatother

\begin{document}

\begin{dialogue}
    \speech{Minka}
        |quietly|Hello, are you there? |shouting| Is anyone there?

    \speech{Tobi}
        |annoyed|  Oh, don't be so fearful.
        You know I'm here!
        Just don't concentrate on what you're doing and you'll be fine.

        |rambling|
        It's not like a lot could go wrong anyway.
        I mean, it's just a program, your computer won't literally explode.
        If you somehow mess it up completely, we'll just reinstall it and you can try again.
        |resigned| But okay, I'll explain it to you again.

        |lecturing| In the \verb|dialogue| environment, you just write \verb|\speech|, then a pair of braces containing the name of the person speaking\dots
        |pausing| Good, like that.
        Now you just type on what they should say.
        Close the brace, and |happily| you're done.
\end{dialogue}

\end{document}
3
  • I like your second solution. | is as good for my users as [...] and it seems way more stable. However the \begin ... \end might already be too complex. I'll check if I can define my \says in a way that it could handle that internally (e.g. \newcommand\says[2]{\begin{dialogue}\speech{#1}#2\end{dialogue}}... One reason for this is: the less the users need to write the more likely is it, that they will use latex (instead of Google Docs and mess up all the layout)
    – Tobi
    Jun 19, 2019 at 13:05
  • @Tobi While it is possible to define such a syntax, I want to strongly discourage you from taking that path. You would either have to do a lot of low level programming to account for all the ways your users might use slightly different syntax than you intended, or you would still have to accept all \catcodes to be frozen, even if you adjust | first. It is much better to just teach your users what an environment is.
    – schtandard
    Jun 19, 2019 at 15:08
  • @Tobi As a more general point, I do not believe the way to get people to use LaTeX is to require them to use as few syntactical constructs as possible, but rather to design a clear interface that reflects the semantics of the content that is to be typeset. That is the fundamental strength of LaTeX, after all. (LaTeX has already done most of the work for you, here, compared to its foundation TeX. If you provide the preamble, your users will only have to learn the rules for spaces and paragraphs and a select number of macros and environments, with a highly standardized syntax.)
    – schtandard
    Jun 19, 2019 at 15:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .