1

In the package that I have been developing, I use the \perfectunary command from the Perfectcut package to get auto-growing brackets that increase in size depending on the number of nested brackets inside them. On the whole, this works extremely well and generally gives me results I am delighted with (occasionally the larger brackets look silly, but it usually noticeably enhances legibility). The only problem I have come across is that anything defined with perfectunary does not seem to allow line breaks at all. Most of the time that isn't a problem, but sometimes there will be something using nested brackets that ends up simply too long for one line, and the only option to deal with I know of is to use line breaks.

Looking at the source for Perfectcut, it seemingly implements versions of \left and \right, which I guess are the reason for the inability to break across lines. I'm afraid that the LaTeX code for the package is a bit beyond me, however. I'll also point out one thing that I have seen in its documentation, about reimplementing the sizes of delimiters (in section 5). It sounds like that could be a useful behaviour to retain, though I am in no way desperate to keep using Perfectcut if there is a handy alternative (for that matter, I'm also not above borrowing the bits of it that are relevant for me and discarding the rest, if that makes sense).

My question is, is there any way that I can replicate the auto-growing effect where it depends upon the nesting level, but which also permits manually inserted line breaks as needed? Automatic line breaking would be wonderful, but I imagine that's much too complex. Since the overly long lines are usually something that can be left until almost the last minute, and there are normally not too many of them, a heavily manual process is acceptable.

I have looked around at various delimiter questions on here, such this, this, this and this, but I am afraid that again the answers there are a bit beyond me. My best guess is that the best (or, at least, almost certainly the easiest) solution to my current problem would be to use the \DeclarePairedDelimiter command from Mathtools, but I'm not quite managing to put the pieces together to achieve what I want.

To give some idea (hopefully) of what I am aiming at, I have prepared the below MWE:

\documentclass[a4paper]{article}

\usepackage{amsmath}
\usepackage{perfectcut}
\usepackage{framed}
\usepackage{changepage}

\makeatletter

\newenvironment{cpruleset}
{\begin{framed}\begin{adjustwidth}{-1.0em}{-1.0em}
\renewcommand{\arraystretch}{1.1}\[\begin{array}{lllllr}}
{\end{array}\]\end{adjustwidth}\end{framed}}

\newcommand{\cprule}[5]{
    \refstepcounter{cpsystems@RuleNum}
    #1 & #2 & \rightarrow_{#3} & #4 & #5 & \hspace{1.5em}(\thecpsystems@RuleNum)\\
}

\newcommand*{\cpfunc}[2]{
#1\perfectunary{IncreaseHeight}{(}{)}{#2}
}

\newcommand*{\cpfuncms}[2]{
#1\perfectunary{IncreaseHeight}{\{}{\}}{#2}
}

\newcommand*{\cpfuncalt}[2]{
#1(#2)
}

\newcommand*{\cpfuncmsalt}[2]{
#1\{#2\}
}

\newcommand*{\cppromoter}[1]{
    & & & & ~ \quad ~ | ~ #1 & \\
}

\newcounter{cpsystems@RuleNum}

\makeatother

\title{MWE}
\author{jcoo092}

\begin{document}
\maketitle

\section{First section}

    \begin{cpruleset}
    
        \cprule{s_1}{\cpfunc{v}{\cpfunc{v}{R}Y}}{1}{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{Y} \; \cpfunc{p}{\cpfunc{h}{R}\cpfunc{p}{\,}}} \; \cpfunc{c}{\lambda}}
        
        \cprule{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{\,} \; \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}} \; \cpfunc{c}{C}}}
%       \cprule{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{\,} &&\\& \enskip \; \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}} \; \cpfunc{c}{C}}}
        {+}{s_3}{\cpfunc{z}{\cpfunc{p}{\cpfunc{h}{R} \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}}}} \; \cpfuncms{c}{W}}
        \cppromoter{\cpfunc{e}{\cpfunc{f}{F} \; \cpfunc{t}{T} \; \cpfunc{c}{W}}}
        
        \cprule{s_2}{}{+}{s_2}
        {\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{Z} \; \cpfunc{p}{\cpfunc{h}{T} \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}}} \; \cpfunc{c}{CW}}}
        \cppromoter{\cpfunc{s}{\cpfunc{r}{R} \; \cpfuncms{u}{\cpfunc{v}{T}Z} \; \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}} \; \cpfunc{c}{C}}}
        \cppromoter{\cpfunc{e}{\cpfunc{f}{F} \; \cpfunc{t}{T} \; \cpfunc{c}{W}}}
        
        \cprule{s_2}{\cpfunc{s}{\_}}{+}{s_2}{}
        
        \cprule{s_3}{}{1}{s_4}{\cpfunc{p'}{P} \quad \cpfunc{c'}{1D}}
        \cppromoter{\cpfunc{z}{\cpfunc{p}{P \; \cpfunc{c}{1D}}}}

    \end{cpruleset}
    \newpage
    \begin{cpruleset}
    
        \cprule{s_1}{\cpfuncalt{v}{\cpfuncalt{v}{R}Y}}{1}{s_2}{\cpfuncalt{s}{\cpfuncalt{r}{R} \; \cpfuncalt{u}{Y} \; \cpfuncalt{p}{\cpfuncalt{h}{R}\cpfuncalt{p}{\,}}} \; \cpfuncalt{c}{\lambda}}
        
        \cprule{s_2}{\cpfuncalt{s}{\cpfuncalt{r}{R} \; \cpfuncalt{u}{\,} &&\\& \enskip \; \cpfuncalt{p}{\cpfuncalt{h}{F}\cpfuncalt{p}{P}} \; \cpfuncalt{c}{C}}}
        {+}{s_3}{\cpfuncalt{z}{\cpfuncalt{p}{\cpfuncalt{h}{R} \cpfuncalt{p}{\cpfuncalt{h}{F}\cpfuncalt{p}{P}}}} \; \cpfuncmsalt{c}{W}}
        \cppromoter{\cpfuncalt{e}{\cpfuncalt{f}{F} \; \cpfuncalt{t}{T} \; \cpfuncalt{c}{W}}}
        
        \cprule{s_2}{}{+}{s_2}
        {\cpfuncalt{s}{\cpfuncalt{r}{R} \; \cpfuncalt{u}{Z} &\\&&&& \enskip \; \cpfuncalt{p}{\cpfuncalt{h}{T} \cpfuncalt{p}{\cpfuncalt{h}{F} \cpfuncalt{p}{P}}} \; \cpfuncalt{c}{CW}}}
        \cppromoter{\cpfuncalt{s}{\cpfuncalt{r}{R} \; \cpfuncmsalt{u}{\cpfuncalt{v}{T}Z} &\\&&&& \qquad \enskip \; \cpfuncalt{p}{\cpfuncalt{h}{F} \cpfuncalt{p}{P}} \; \cpfuncalt{c}{C}}}
        \cppromoter{\cpfuncalt{e}{\cpfuncalt{f}{F} \; \cpfuncalt{t}{T} \; \cpfuncalt{c}{W}}}
        
        \cprule{s_2}{\cpfuncalt{s}{\_}}{+}{s_2}{}
        
        \cprule{s_3}{}{1}{s_4}{\cpfuncalt{p'}{P} \quad \cpfuncalt{c'}{1D}}
        \cppromoter{\cpfuncalt{z}{\cpfuncalt{p}{P \; \cpfuncalt{c}{1D}}}}

    \end{cpruleset}
    
\(\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{Z} \; \\ \cpfuncms{p}{\cpfunc{h}{T} \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}}} \; \cpfunc{c}{CW}}\)

\end{document}

The aim is to have the auto-growing behaviour from the first cpruleset, while also using the line breaks in the second cpruleset to make everything fit within the page margins. I have included both cases with parentheses and curly braces, to highlight the fact that different types of brackets can be (and are in practice) mixed and matched within a given nested set. In case it makes a difference, in my head I am aiming at an approach where the relevant opening and closing brackets only open or close their own line, rather than growing to wrap every line inside themselves, but if the latter is much easier to accomplish I'm happy with it too.

The newcommands are essentially copy-pasted from the .sty file for my package, and then simplified down to the minimum possible, except for cpfuncalt and cpfuncmsalt --- I introduced them specifically for the purposes of the MWE. The top cpruleset is how I would normally set out this sort of thing using Perfectcut, and the bottom cpruleset is the same as the top one, but with the uses of perfectunary removed so that I can demonstrate the sort of line breaks I would like to insert.

Both the last line outside the cprulesets, and the commented-out line in the top one, are there to demonstrate the problems I experience. The bottom line has a line break command in it that seemingly gets ignored. If I uncomment the commented-out line, and comment out the line above it (which is the same but without a line break), then I get an error that reads:

> ! Misplaced alignment tab character &. <argument> ... \cpfunc {r}{R}
> \; \cpfunc {u}{\,} &
>                                                   &\\& \enskip \; \cpfunc {p...
> l.58 ...{h}{F}\cpfunc{p}{P}}}} \; \cpfuncms{c}{W}}

I assume that this occurs because the line break has been ignored, so the extra &s included to make the array line up properly appear to be part of the same line.

1 Answer 1

1

After posting this question, I went back and had another go at using Mathtools' DeclarePairedDelimiter command. I ended up coming with this:

\DeclarePairedDelimiter{\cpparens}{\lparen}{\rparen}

and

\newcommand*{\cpfuncalt}[3][]{
  #2\cpparens[#1]{#3}
}

which led to this revised MWE:

\documentclass{article}

\usepackage{mathtools}
\usepackage{perfectcut}
\usepackage{framed}
\usepackage{changepage}

\makeatletter

\newenvironment{cpruleset}
{\begin{framed}\begin{adjustwidth}{-1.0em}{-1.0em}
\renewcommand{\arraystretch}{1.1}\[\begin{array}{lllllr}}
{\end{array}\]\end{adjustwidth}\end{framed}}

\newcommand{\cprule}[5]{
    \refstepcounter{cpsystems@RuleNum}
    #1 & #2 & \rightarrow_{#3} & #4 & #5 & \hspace{1.5em}(\thecpsystems@RuleNum)\\
}

\DeclarePairedDelimiter{\cpparens}{\lparen}{\rparen}
\DeclarePairedDelimiter{\cpcurls}{\lbrace}{\rbrace}

\newcommand*{\cpfunc}[2]{
#1\perfectunary{IncreaseHeight}{\lparen}{\rparen}{#2}
}

\newcommand*{\cpfuncms}[2]{
#1\perfectunary{IncreaseHeight}{\lbrace}{\rbrace}{#2}
}

\newcommand*{\cpfuncalt}[3][]{
#2\cpparens[#1]{#3}
}

\newcommand*{\cpfuncmsalt}[3][]{
{#2\cpcurls[#1]{#3}}
}

\newcommand*{\cppromoter}[1]{
    & & & & ~ \quad ~ | ~ #1 & \\
}

\newcounter{cpsystems@RuleNum}

\makeatother

\title{MWE}
\author{jcoo092}

\begin{document}
\maketitle

\section{First section}

    \begin{cpruleset}
    
        \cprule{s_1}{\cpfunc{v}{\cpfunc{v}{R}Y}}{1}{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{Y} \; \cpfunc{p}{\cpfunc{h}{R}\cpfunc{p}{\,}}} \; \cpfunc{c}{\lambda}}
        
        \cprule{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{\,} \; \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}} \; \cpfunc{c}{C}}}
%       \cprule{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{\,} &&\\& \enskip \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}} \; \cpfunc{c}{C}}}
        {+}{s_3}{\cpfunc{z}{\cpfunc{p}{\cpfunc{h}{R} \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}}}} \; \cpfuncms{c}{W}}
        \cppromoter{\cpfunc{e}{\cpfunc{f}{F} \; \cpfunc{t}{T} \; \cpfunc{c}{W}}}
        
        \cprule{s_2}{}{+}{s_2}
        {\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{Z} \; \cpfunc{p}{\cpfunc{h}{T} \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}}} \; \cpfunc{c}{CW}}}
        \cppromoter{\cpfunc{s}{\cpfunc{r}{R} \; \cpfuncms{u}{\cpfunc{v}{T}Z} \; \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}} \; \cpfunc{c}{C}}}
        \cppromoter{\cpfunc{e}{\cpfunc{f}{F} \; \cpfunc{t}{T} \; \cpfunc{c}{W}}}
        
        \cprule{s_2}{\cpfunc{s}{\_}}{+}{s_2}{}
        
        \cprule{s_3}{}{1}{s_4}{\cpfunc{p'}{P} \quad \cpfunc{c'}{1D}}
        \cppromoter{\cpfunc{z}{\cpfunc{p}{P \; \cpfunc{c}{1D}}}}

    \end{cpruleset}
    \newpage
    \begin{cpruleset}
    
        \cprule{s_1}{\cpfunc{v}{\cpfunc{v}{R}Y}}{1}{s_2}{\cpfunc{s}{\cpfunc{r}{R} \; \cpfunc{u}{Y} \; \cpfunc{p}{\cpfunc{h}{R}\cpfunc{p}{\,}}} \; &\\&&&& \cpfunc{c}{\lambda}}
        
        \cprule{s_2}{\cpfuncalt[\Big]{s}{\cpfunc{r}{R} \; \cpfunc{u}{\,} &&\\& \enskip \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}} \; \cpfunc{c}{C}}}
        {+}{s_3}{\cpfunc{z}{\cpfunc{p}{\cpfunc{h}{R} \cpfunc{p}{\cpfunc{h}{F}\cpfunc{p}{P}}}} \; &\\&&&& \cpfuncms{c}{W}}
        \cppromoter{\cpfunc{e}{\cpfunc{f}{F} \; \cpfunc{t}{T} \; \cpfunc{c}{W}}}
        
        \cprule{s_2}{}{+}{s_2}
        {\cpfuncalt[\bigg]{s}{\cpfunc{r}{R} \; \cpfunc{u}{Z} &\\&&&& \enskip \cpfunc{p}{\cpfunc{h}{T} \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}}} &\\&&&& \enskip \cpfunc{c}{CW}}}
        \cppromoter{\cpfuncalt[\Big]{s}{\cpfunc{r}{R} \; \cpfuncmsalt[\big]{u}{\cpfunc{v}{T}Z} &\\&&&& \qquad \enskip \cpfuncalt[\big]{p}{\cpfunc{h}{F} \cpfunc{p}{P}} \; \cpfunc{c}{C}}}
        \cppromoter{\cpfunc{e}{\cpfunc{f}{F} \; \cpfunc{t}{T} \; \cpfunc{c}{W}}}
        
        \cprule{s_2}{\cpfunc{s}{\_}}{+}{s_2}{}
        
        \cprule{s_3}{}{1}{s_4}{\cpfunc{p'}{P} \quad \cpfunc{c'}{1D}}
        \cppromoter{\cpfunc{z}{\cpfunc{p}{P \; \cpfunc{c}{1D}}}}

    \end{cpruleset}
    
\(\cpfuncalt[\bigg]{s}{\cpfunc{r}{R} \; \cpfunc{u}{Z} \; \\\ \cpfuncms{p}{\cpfunc{h}{T} \cpfunc{p}{\cpfunc{h}{F} \cpfunc{p}{P}}} \; \cpfunc{c}{CW}}\)

\end{document}

Using this extra command, I am able to keep using the command defined using Perfectcut (cpfunc) most of the time, and only replace some commands with one involving manual resizing. Usually, only the outermost set of brackets necessitate a line break, so I can normally get away with only adjusting them manually.

This achieves the minimum of what I need, but I'm not a big fan of it. Improvements on it that I would like to achieve include:

  • Using Perfectcut's size measures, instead of AMSMath's handful, to avoid any inconsistency between sizings.
  • Some automated way of counting the number of nestings and setting things appropriately, to avoid a potentially error-prone manual process.
  • I honestly doubt I am using the resources provided by Mathtools very effectively. I'm sure there's a better/more efficient/more useful way to define these things, but my understanding of Mathtools is far too limited to spot it.
  • Some way of ensuring that the newline starts indented at least as far as the rest of the enclosing term above it, and perhaps a little more. Right now, I'm basically just mucking about with spacings inserted manually to get the same effect, but I'm sure there must be a better way to do it (possibly some sort of phantom space?)
  • A way to indicate that the contents inside a set of brackets have been shifted onto another line. Currently, I am thinking of including a character like U+21B5 or similar, but I'm struggling to work out how to include that (though, this point is probably a separate question).
  • I suspect that the cpfuncalt command as defined would be awfully prone to falling foul of weird little errors that the error reports don't make clear. It would be good to make it more able to help users identify where and what any errors might be, but I have no idea what to do for that.

If anyone wants to try to address any of the above points, or improve this in another fashion, I would be very grateful.

You must log in to answer this question.

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