What is the best syntax for a hanging indent in math equation blocks?

When I am writing out complex derivations, I often want a “hanging indent” style of alignment to avoid excessive width on the first line, like this:

$
  &1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
  &wide = (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x) \
  &wide = (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) \
  &wide = (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) \
  &wide thick dots.v
$

The cleanest way I know how to do this is by repeating &quad or &wide at the beginning of each line, but I feel like there ought to be a better way. For example, I have tried

$
  &#h(-2em) 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
  &= (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x) \
  &= (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) \
  &= (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) \
  &thick dots.v
$

which kind of works in that it eliminates the repeated &wide, but it means the equation block as a whole isn’t properly centered:

#let red-box(it) = align(center, block(stroke: red + 0.5pt, it))
#red-box[
  $
    &#h(-2em) 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
    &= (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x)
  $
]

I could put the & anchor on some random variable that happens to be near where I want my hanging indent, as in

#red-box[$
  1 + &x + x^2 + x^3 + x^4 + x^5 + x^6 \
  &= (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x)
$]

which works, but hurts my soul.

Is there a better way to do this, or a package that can do it for me?

4 Likes

Have you thought about using a grid or table for this purpose? I think it is possible to define a custom table which implements this to your liking (it has only one column, where the first row is treated specially), and then wrap this table in a function.

1 Like

What you could do is enclose the to be indented variables using #pad, so you only have to indent once for everything instead of per line. Do note that you can’t align &-elements inside the pad-scope with elements outside of it!

#let red-box(it) = align(center, block(stroke: red + 0.5pt, it))
#red-box[
  $
    &1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
    &#pad(left: 2em, $ &= (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x) \
    &= (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) \
    &= (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) \
    &thick dots.v $)
  $
]

1 Like

No, I hadn’t considered that. I’d have to re-enter math mode for each table element, right? Something like this does work, but I don’t love that I have to enter so many $ characters:

#let red-box(it) = align(center, block(stroke: red + 0.5pt, it))
#let hanging-indent-grid(..args) = {
  grid(
    columns: 1,
    stroke: blue + 0.1pt,
    align: left,
    inset: (left: 2em),
    row-gutter: 0.65em,
    grid.cell(inset: (left: 0em), block(args.pos().at(0))),
    ..args.pos().slice(1).map(it => block(it))
  )
}

#red-box(hanging-indent-grid(
  $ 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 $,
  $ = (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x) $,
  $ = (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) $,
  $ = (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) $,
  $ dots.v $
))

Thanks for the idea!

Yes, I like pad for this! Adding to your work a little, by adding a show rule, I avoid the need for ampersands in the indented block at all:

#let red-box(it) = align(center, block(stroke: red + 0.5pt, it))

#let eqn-indent(it) = {
  show math.equation: set align(left)
  pad(left: 2em, it)
}

#red-box[$
  &1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
  &#eqn-indent[$
    = (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + 0 + 0 + 0)/(x) \
    = (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) \
    = (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) \
    thick dots.v
  $]
$]

Thank you!

Here’s another kind of cursed solution:

#let new-eq = state("new-eq", true)

#let align = context {  // or whatever name you prefer
  if not new-eq.get() {
    $&$.body + math.wide
  } else {
    $&$.body
  }
} + new-eq.update(false)

#show math.equation: it => {
  new-eq.update(true)
  it
}

$
  align 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
  align = (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x) \
  align = (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) \
  align = (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) \
  align thick dots.v
$

It works by inserting spaces after every align, meaning the output is identical to the way you were doing it initially and so it works even if you inserted more alignment points in your equations.

Alas, show rules on alignment points are not permitted. If they were, we could replace the align variable with a show rule

#show math.equation: it => {
  if not it.has("label") or it.label != <hang-first> {
    return it
  }
  show $&$.body: context {
    if not new-eq.get() {
      $&$.body + math.wide
    } else {
      $&$.body
    }
  } + new-eq.update(false)
  it
}

$
  & 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 \
  & = (x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7)/(x) \
  & = (x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8)/(x^2) \
  & = (x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9)/(x^3) \
  & thick dots.v
$<hang-first>
1 Like