Is it possible to detect math mode?

Can some content detect whether it is inside a math.equation or not?

#let func(arg) = context {
  // distinguish the two cases below
}

#func[H]
$func(H)$

I very much suspect that this is not possible but maybe I am overlooking something. One could indeed check text.font but that does not really help because it is not possible to write a comparison with the font that is set for either general text or equations.

Well, except I can think of a really dumb check relying on the fact that most math-capable fonts contain the word “Math” in their name:

#let func(arg) = context {
  let in-math = "math" in text.font
}

#func[H]
$func(H)$

Maybe there should be a Typst feature that makes it possible to check whether the font is a math-font or even better the direct answer to this question.

2 Likes

Yes. You can use the classic in-* boolean state approach used for different supplement in figure/reference (in-ref), or fixing trailing numbering suffix for headings in-document/reference (in-heading), or short/long figure caption in document/outline (in-outline).

#set page(width: auto, height: auto, margin: 1pt)

#let in-math = state("in-math", false)
#show math.equation: it => in-math.update(true) + it + in-math.update(false)
#let func(arg) = context if in-math.get() [\$$arg$\$] else [#arg]

#func[this]

$func(t h i s)$

$
  func(t h i s)
$

1 Like

Thank you for your suggestion @Andrew.

I should add that this is for a package which is why I cannot use global show rules. With user-defined types, it would otherwise be possible to write

#set func(math: false)
#show math.equation: set func(math: true)

This is basically the same as your approach.

The point is that this would require the user to place this particular show rule in their document and I would not be able to automate it away into the package.

2 Likes

The show-set rule needs true.

Yes indeed, but we don’t have custom elements, so all that is left are “cheap imitations.”

Can’t automate, but Codly also has the issue of needing to invoke a show rule at a particular point in preamble, so you can also have an “everything show rule,” if there are no other ways. This one must go at the bottom of preamble as it’s constructive and will disappear if destructive show rules will follow.

Can’t Elembic handle this use case?

P.S. The current title question has been already answered.

1 Like

The plain answer to the question as stated is that the contents are different in the test case. But I suspect this is not what you are looking for?

#let math-symbol = $x$.body.func()
#let func(arg) = context {
  // distinguish the two cases below
  let info = (repr(arg), arg.func(), arg.fields())
  let is-math = arg.func() == math-symbol
  if is-math [You are math] else [You are not math]
}

#func[H]
$func(H)$

bild

A different question - maybe the one I expected - would maybe use empty content and just try to understand if we’re in an equation based on the style context alone?

1 Like

The show-set rule needs true.

Oops, typical copy-paste error. I fixed it.

I did already think of these potential workarounds. Unfortunately, for me this does not solve the problem because it is something that is needed only in specific cases, so I don’t want to convince users to add this show rule which is redundant in 99% of the cases.

This is really smart. I was searching for something along this line. Unfortunately, arg will often be a string in which case there is no difference (I believe).

I also thought of measuring something in the context and checking whether it’s somehow measured differently in math mode (due to sizing and spacing) but I could not find anything.

Yeah that seems hopeless. You can produce the same symbol content kind outside an equation too, it’s just that H doesn’t :wink:. For example using #func[#sym.plus].

I didn’t find any way there either. Especially since measure doesn’t interact with the layout of the current equation, just with the inherited style.

Is it possible to insert markers in what you are producing, and introspect the content you yourself produce, and act upon that (using query/state)? When we can’t find anything in the style, maybe we need to involve the content that’s being produced.

But if the complexity is unreasonable, requiring using a global show rule seems like a better solution.

Even if it’s basically useless, users don’t know that, and it’s not uncommon to have a package show rule. Since it doesn’t affect the performance (should be fine?), you won’t lose anything. Plus it’s the easiest and one of well known/used strats (and others looks way too complex etc.). The choice is yours.

Here is a deep hack to check if we are inside an equation or not.

We use the fact that $$ inside an equation is absorbed into the parent equation, but $$ in other contexts remains a standalone equation… (This might not be true, at least it’s not visible like that at the point of the query, but that’s how I thought about it. More true: only the outer equation is queryable?)

#set text(font: "New Computer Modern Math", weight: 450)
#let func(arg) = {
  let _info = (repr(arg), arg.func(), arg.fields())
  show <_mymeta>: mt => {
    let loc = mt.location()
    let last-eq = query(selector(math.equation).before(loc)).last(default: none)
    let am-i-alone = last-eq != none and last-eq.body.func() == metadata and last-eq.body == mt
    show: box
    if am-i-alone {
      [I was not math (but now I am)]
    } else {
      [I was in math, and I'm still there]
    }
  }
  math.equation([#metadata(none)<_mymeta>])
}

#func[\+]

$func(+)$


bonus version, iteration #2
#let func(arg) = {
  let _info = (repr(arg), arg.func(), arg.fields())
  context {
    // use bluss's patented range query
    let result = query(
      selector(<_equation-canary>)
      .after(here())
      .before(selector(<_end-marker>).after(here()))
    ).first(default: none)
    if result != none {
      [I am not in math]
    } else {
      [I am in math]
    }
  }
  [$#metadata(none)$<_equation-canary>]
  [#metadata(none)<_end-marker>]
}

#func[\+]

$func(+)$
3 Likes

last-eq.body.func() == metadata seems useless if your label is only used with metadata anyway. The next check already implies metadata, too. Also can s/([])/[] at math.equation.

I would assume query will tank the performance if this used a lot and there are a lot of equations.