Why does a display equation break a paragraph?

I found that the “display” equations themselves also break paragraphs. For example,

#set par(hanging-indent: 3em)
#lorem(50)
$ display(y=a x + b) $
#lorem(100)

This code block is renderred as

image

The output would not change even with the following code

#lorem(50) $ display(y=a x + b) $ #lorem(100)

However, a “display” equation should be a part of a paragraph, if there is no blank lines around it. This is also how LaTeX and Word deals with display equations.


P.S. To be honest, I’m afraid the priority of the paragraph now is a bit too low. Anything showing by a block can break the paragraph.

The break actually appears because you’re using block math $ ... $ instead of inline math $...$ (notice the spaces on the left and right).

This

#lorem(50) $display(y=a x + b)$ #lorem(100)

will render as
image

From what I understand, you do want the equation to be separate in terms of a new line, but the paragraph should remain the same. As you already assumed correctly, all block level elements currently break the paragraph, so this isn’t really possible at the moment.

If you really want to, you can work around this by wrapping the equation in a box and adding line breaks manually. The following may not work well when the equation is at the start or end of a paragraph, but then you can also just use normal equations.

#set par(hanging-indent: 3em)

#let par-equation(eq) = {
  linebreak()
  box(inset: (y: 0.5em), width: 1fr, eq)
  linebreak()
}

#lorem(50)
#par-equation($ a + b $)
#lorem(50)

3 Likes

Oh I see, I wonder if there are any good arguments for either case, i.e. merging the paragraphs vs keeping them separate.

I reckon it would have positive impact in most cases to actually merge math blocks into its surrounding paragraphs, if there’s no issue for this you could create one.

Could you elaborate on this?

If a block equation is semantically part of a paragraph, as was intended here, then the current behavior introduces a parbreak regardless of intention. Depending on set and show rules in effect this could then cause a few hiccups:

  • For first-line-indent being set this, the next paragraph to get and indentation even when the user did not intend so.
  • Spacing would likely be incorrect.
  • Something like tagged PDF export would not place the correct tags, resulting in the paragraph being split up for the PDF reader too.

So, I’m thinking, perhaps we should treat math blocks as part of a paragraph unless a parbreak is explicitly added, i.e.:

#[
  Markup
  $ block $
  Markup
]

// becomes

#par[
  Markup
  $ block $
  Markup
]

and

#[
  Markup
  
  $ block $
  
  Markup
]

// becomes

#par[Markup]
$ block $
#par[Markup]

At least for me, math is often used in a flow of text without intention of breaking up the surrounding paragraphs, just like shown in the question.

Somewhat related, we also treat the spacing for lists differently depending on the previous line:

Markup
- List

// is not the same as

Markup

- List

An alternative could be simply wrapping the whole paragraph in par of course:

#par[
  Markup
  $ block $
  Markup
]
6 Likes

Indeed in books display math is usually part of the paragraph, it’s even common for it to appear in the middle of a sentence. Some random examples:

Spivak’s calculus:

image

Bishop’s Pattern Recognition and Machine Learning:

So I agree that display equations $ ... $ should not start a new paragraph by themselves.

5 Likes

This is essentially the same as this issue (though I originally phrased it for lists)

I agree with you. I think this should be a language-level feature, although we can create something like the environment equation in LaTeX to wrap our equations. Moreover, if you want to display equation numbers, there would be another issue,

We need extra effort to adjust the numbers.

I think so. As I said, the priority of the paragraph seems to be too low and a block can easily break the paragraph flow. But fixing this would be a breaking change. I’m afraid that it would not happen in a short time.

Looks like you used box(width: 100%) instead of box(width: 1fr).

Thanks. I finally figured out how to fix this issue.

First of all, I apologize for reviving this thread.

With v0.13, indenting every paragraph is now easy, which is a good thing. However, as @Tinger and @Aegon mentioned, math blocks often appear within paragraphs. While using the box function is a solution of sorts, it has some flaws. For example, it doesn’t work well with the equate package, which is necessary for sub-equations.

Is there a way to avoid parbreak after equation blocks? Thank you!

We discussed this on discord a while back and @Eric posted this way of controlling paragraph indent after an equation. This doesn’t change what the paragraphs are (the paragraphs are still separate), it just changes the indent.

(Discord link)

#set par(first-line-indent: (amount: 2em, all: true))

#show math.equation: it => it + [#[ #[]<eq-end>]]
#show par: it => {
  if it.first-line-indent.amount == 0pt {
    // Prevent recursion.
    return it
  }
  
  context {
    let eq-end = query(selector(<eq-end>).before(here())).at(-1, default: none)
    if eq-end == none { return it }
    if eq-end.location().position() != here().position() { return it }
    
    // Paragraph start aligns with end of last equation, so recreate
    // the paragraph, but without indent.
    let fields = it.fields()
    let body = fields.remove("body")
    return par(
      ..fields,
      first-line-indent: 0pt,
      body
    )
  }
}

#lorem(15)

$ x = 10 $

#lorem(15)

#lorem(15)

I also experimented with a state based solution to add directives parindent and noparindent. While those look good on the surface, some shortcomings quickly become apparent. Typst Discord link, only

3 Likes

This reminds me of an idea: is it possible to rearrange elements into a series of blocks divided by the parbreak()? Like Touying, which convert the document into a series of slides (pages). Perhaps we can convert the document into a series of blocks, containing consecutive paragraphs, equations, lists, etc?

1 Like

I did a very very basic implementation of my idea of arranging paragraphs into blocks.

#set par(first-line-indent: (amount: 2em, all: false), hanging-indent: 0em)

#let eb-trim(children) = {
  let elem-first = children.position(e => e != [ ])
  if not elem-first == none {
    children.slice(elem-first)
  } else {
    children
  }
}

#let blocking(body) = context {
  let hanging-indent = par.hanging-indent
  set par(first-line-indent: (amount: 0em, all: par.first-line-indent.all))
  let elem-blocks = body.children.split(parbreak()).filter(eb => eb.filter(e => e != [ ]).len() > 0).map(eb-trim)
  for (ieb, eb) in elem-blocks.enumerate() {
    let fli = par.first-line-indent.amount
    if not par.first-line-indent.all {
      if ieb > 0 {
        let pre-eb = elem-blocks.at(ieb - 1)
        if pre-eb.len() > 0 and pre-eb.last().func() == heading {
          fli = 0em
        }
      } else if ieb == 0 {
        fli = 0em
      }
    }
    if eb.first().func() == heading {
      eb.join()
    } else {
      block(
        stroke: red + 1pt,
        width: 100%,
        above: if ieb > 0 { par.spacing } else { par.leading },
        {
          set par(spacing: par.leading)
          set list(spacing: par.leading, indent: par.hanging-indent)
          set enum(spacing: par.leading, indent: par.hanging-indent)
          eb.enumerate().map(((ie, e)) => {
            if e.func() == text {
              h(if ie > 0 { hanging-indent } else { fli })
              e
            } else { e }
          }).fold([], (a, b) => a + b)
        }
      )
    }
  }
}

And a document would looks like

#show: blocking

#lorem(30)
- #lorem(10)
- #lorem(10)
#lorem(10)

#lorem(10)
$ y = a x + b $
#lorem(20)

= Heading

#lorem(10)

+ #lorem(10)
+ #lorem(10)

#lorem(10)

In this case, consecutive items are in a block. Inserting parbreak() would break the block.

Points to improve:

  1. Instead of using split(), something to “eat” elements and generate blocks would be better.
  2. For special cases, it only handles header. Other special elements remain to be improved.
  3. Every basic elements need to be styled in a “block”. For now, only list and enum are checked.

Anyway, I hope it can be a language-level feature to put consecutive items in a block/box.

2 Likes